<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/blog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/blog/css/main.css">


<link rel="stylesheet" href="/blog/lib/font-awesome/css/font-awesome.min.css">
  <link rel="stylesheet" href="/blog/lib/pace/pace-theme-bounce.min.css">
  <script src="/blog/lib/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"ipvb.gitee.io","root":"/blog/","scheme":"Gemini","version":"7.7.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false,"b2t":true,"scrollpercent":true},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="翻译至 Redis 命令参考  一、Key（键）1.DELDEL key [key …] 删除给定的一个或多个 key 。 不存在的 key 会被忽略。  可用版本：  &#x3D; 1.0.0   时间复杂度： O(N)， N 为被删除的 key 的数量。删除单个字符串类型的 key ，时间复杂度为O(1)。删除单个列表、集合、有序集合或哈希表类型的 key ，时间复杂度为O(M)， M 为以上数据结构">
<meta property="og:type" content="article">
<meta property="og:title" content="Redis所有命令+集群（翻译转载）">
<meta property="og:url" content="https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/index.html">
<meta property="og:site_name" content="Chenyumeng的博客">
<meta property="og:description" content="翻译至 Redis 命令参考  一、Key（键）1.DELDEL key [key …] 删除给定的一个或多个 key 。 不存在的 key 会被忽略。  可用版本：  &#x3D; 1.0.0   时间复杂度： O(N)， N 为被删除的 key 的数量。删除单个字符串类型的 key ，时间复杂度为O(1)。删除单个列表、集合、有序集合或哈希表类型的 key ，时间复杂度为O(M)， M 为以上数据结构">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-03-15T08:15:53.000Z">
<meta property="article:modified_time" content="2020-03-16T09:42:11.801Z">
<meta property="article:author" content="Chenyumeng">
<meta property="article:tag" content="笔记">
<meta property="article:tag" content="linux">
<meta property="article:tag" content="CentOS7">
<meta property="article:tag" content="Redis">
<meta property="article:tag" content="数据库">
<meta property="article:tag" content="NoSQL">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true
  };
</script>

  <title>Redis所有命令+集群（翻译转载） | Chenyumeng的博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

  <link rel="stylesheet" href="/dist/css/share.min.css">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <div>
      <a href="/blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Chenyumeng的博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <p class="site-subtitle">热爱自己，热爱生活</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
        <li class="menu-item menu-item-home">

    <a href="/blog/" rel="section"><i class="fa fa-fw fa-home"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/blog/about/" rel="section"><i class="fa fa-fw fa-user"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/blog/tags/" rel="section"><i class="fa fa-fw fa-tags"></i>标签<span class="badge">29</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/blog/categories/" rel="section"><i class="fa fa-fw fa-th"></i>分类<span class="badge">1</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blog/archives/" rel="section"><i class="fa fa-fw fa-archive"></i>归档<span class="badge">32</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>

</nav>
  <div class="site-search">
    <div class="search-pop-overlay">
  <div class="popup search-popup">
      <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocorrect="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

  </div>
</div>

  </div>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/chen-yumeng" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content">
            

  <div class="posts-expand">
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block " lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/touxiang.JPG">
      <meta itemprop="name" content="Chenyumeng">
      <meta itemprop="description" content="用来记录自己学习中所遇到的问题以及如何解决和自己所学知识的理解">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Chenyumeng的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Redis所有命令+集群（翻译转载）
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2020-03-15 16:15:53" itemprop="dateCreated datePublished" datetime="2020-03-15T16:15:53+08:00">2020-03-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-03-16 17:42:11" itemprop="dateModified" datetime="2020-03-16T17:42:11+08:00">2020-03-16</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/%E5%AD%A6%E4%B9%A0/" itemprop="url" rel="index"><span itemprop="name">学习</span></a>
                </span>
            </span>

          
            <span id="/blog/2020/03/15/Redis_translate/" class="post-meta-item leancloud_visitors" data-flag-title="Redis所有命令+集群（翻译转载）" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span><br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="fa fa-file-word-o"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>161k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="fa fa-clock-o"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>2:26</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <blockquote>
<p>翻译至</p>
<p><a href="http://doc.redisfans.co" target="_blank" rel="noopener"><em>Redis 命令参考</em></a></p>
</blockquote>
<h1 id="一、Key（键）"><a href="#一、Key（键）" class="headerlink" title="一、Key（键）"></a>一、Key（键）</h1><h2 id="1-DEL"><a href="#1-DEL" class="headerlink" title="1.DEL"></a>1.DEL</h2><p><strong>DEL key [key …]</strong></p>
<p>删除给定的一个或多个 <code>key</code> 。</p>
<p>不存在的 <code>key</code> 会被忽略。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为被删除的 <code>key</code> 的数量。删除单个字符串类型的 <code>key</code> ，时间复杂度为O(1)。删除单个列表、集合、有序集合或哈希表类型的 <code>key</code> ，时间复杂度为O(M)， <code>M</code> 为以上数据结构内的元素数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>被删除 <code>key</code> 的数量。</p>
</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">  删除单个 key</span></span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET name huangz</span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DEL name</span></span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 删除一个不存在的 key</span></span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> EXISTS phone</span></span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DEL phone <span class="comment"># 失败，没有 key 被删除</span></span></span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 同时删除多个 key</span></span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET name <span class="string">"redis"</span></span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET <span class="built_in">type</span> <span class="string">"key-value store"</span></span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET website <span class="string">"redis.com"</span></span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DEL name <span class="built_in">type</span> website</span></span><br><span class="line">(integer) 3</span><br></pre></td></tr></table></figure>

<a id="more"></a>

<h2 id="2-DUMP"><a href="#2-DUMP" class="headerlink" title="2.DUMP"></a>2.DUMP</h2><p><strong>DUMP key</strong></p>
<p>序列化给定 <code>key</code> ，并返回被序列化的值，使用 <em>RESTORE</em> 命令可以将这个值反序列化为 Redis 键。</p>
<p>序列化生成的值有以下几个特点：</p>
<ul>
<li>它带有 64 位的校验和，用于检测错误， <em>RESTORE</em>在进行反序列化之前会先检查校验和。</li>
<li>值的编码格式和 RDB 文件保持一致。</li>
<li>RDB 版本会被编码在序列化值当中，如果因为 Redis 的版本不同造成 RDB 格式不兼容，那么 Redis 会拒绝对这个值进行反序列化操作。</li>
</ul>
<p>序列化的值不包括任何生存时间信息。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>查找给定键的复杂度为 O(1) ，对键进行序列化的复杂度为 O(N*M) ，其中 N 是构成 <code>key</code> 的 Redis 对象的数量，而 M 则是这些对象的平均大小。如果序列化的对象是比较小的字符串，那么复杂度为 O(1) 。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果 <code>key</code> 不存在，那么返回 <code>nil</code> 。否则，返回序列化之后的值。</p>
</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET greeting <span class="string">"hello, dumping world!"</span></span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DUMP greeting</span></span><br><span class="line">"\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde"</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DUMP not-exists-key</span></span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="3-EXISTS"><a href="#3-EXISTS" class="headerlink" title="3.EXISTS"></a>3.EXISTS</h2><p><strong>EXISTS key</strong></p>
<p>检查给定 <code>key</code> 是否存在。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>若 <code>key</code> 存在，返回 <code>1</code> ，否则返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">redis&gt;</span><span class="bash"> SET db <span class="string">"redis"</span></span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> EXISTS db</span></span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> DEL db</span></span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> EXISTS db</span></span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="4-EXPIRE"><a href="#4-EXPIRE" class="headerlink" title="4.EXPIRE"></a>4.EXPIRE</h2><p><strong>EXPIRE key seconds</strong></p>
<p>为给定 <code>key</code> 设置生存时间，当 <code>key</code> 过期时(生存时间为 <code>0</code> )，它会被自动删除。</p>
<p>在 Redis 中，带有生存时间的 <code>key</code> 被称为『易失的』(volatile)。</p>
<p>生存时间可以通过使用 <em>DEL</em> 命令来删除整个 <code>key</code> 来移除，或者被 <em>SET</em> 和 <em>GETSET</em>命令覆写(overwrite)，这意味着，如果一个命令只是修改(alter)一个带生存时间的 <code>key</code> 的值而不是用一个新的 <code>key</code> 值来代替(replace)它的话，那么生存时间不会被改变。</p>
<p>比如说，对一个 <code>key</code> 执行 <em>INCR</em> 命令，对一个列表进行 <em>LPUSH</em> 命令，或者对一个哈希表执行 <em>HSET</em>命令，这类操作都不会修改 <code>key</code> 本身的生存时间。</p>
<p>另一方面，如果使用 <em>RENAME</em> 对一个 <code>key</code> 进行改名，那么改名后的 <code>key</code> 的生存时间和改名前一样。</p>
<p><em>RENAME</em>命令的另一种可能是，尝试将一个带生存时间的 <code>key</code> 改名成另一个带生存时间的 <code>another_key</code> ，这时旧的 <code>another_key</code> (以及它的生存时间)会被删除，然后旧的 <code>key</code> 会改名为 <code>another_key</code> ，因此，新的 <code>another_key</code> 的生存时间也和原本的 <code>key</code> 一样。</p>
<p>使用 <em>PERSIST</em>命令可以在不删除 <code>key</code> 的情况下，移除 <code>key</code> 的生存时间，让 <code>key</code> 重新成为一个『持久的』(persistent) <code>key</code> 。</p>
<p><strong>更新生存时间</strong></p>
<p>可以对一个已经带有生存时间的 <code>key</code> 执行 <em>EXPIRE</em> 命令，新指定的生存时间会取代旧的生存时间。</p>
<p><strong>过期时间的精确度</strong></p>
<p>在 Redis 2.4 版本中，过期时间的延迟在 1 秒钟之内 —— 也即是，就算 <code>key</code> 已经过期，但它还是可能在过期之后一秒钟之内被访问到，而在新的 Redis 2.6 版本中，延迟被降低到 1 毫秒之内。</p>
<p><strong>Redis 2.1.3 之前的不同之处</strong></p>
<p>在 Redis 2.1.3 之前的版本中，修改一个带有生存时间的 <code>key</code> 会导致整个 <code>key</code> 被删除，这一行为是受当时复制(replication)层的限制而作出的，现在这一限制已经被修复。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功返回 <code>1</code> 。当 <code>key</code> 不存在或者不能为 <code>key</code> 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 <code>key</code> 的生存时间)，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET cache_page &quot;www.google.com&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXPIRE cache_page 30  # 设置过期时间为 30 秒</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL cache_page    # 查看剩余生存时间</span><br><span class="line">(integer) 23</span><br><span class="line"></span><br><span class="line">redis&gt; EXPIRE cache_page 30000   # 更新过期时间</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL cache_page</span><br><span class="line">(integer) 29996</span><br></pre></td></tr></table></figure>

<h2 id="5-EXPIREAT"><a href="#5-EXPIREAT" class="headerlink" title="5.EXPIREAT"></a>5.EXPIREAT</h2><p><strong>EXPIREAT key timestamp</strong></p>
<p>EXPIREAT的作用和 <em>EXPIRE</em> 类似，都用于为 <code>key</code> 设置生存时间。</p>
<p>不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果生存时间设置成功，返回 <code>1</code> 。当 <code>key</code> 不存在或没办法设置生存时间，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET cache www.google.com</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXPIREAT cache 1355292000     # 这个 key 将在 2012.12.12 过期</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL cache</span><br><span class="line">(integer) 45081860</span><br></pre></td></tr></table></figure>

<h2 id="6-KEYS"><a href="#6-KEYS" class="headerlink" title="6.KEYS"></a>6.KEYS</h2><p><strong>KEYS pattern</strong></p>
<p>查找所有符合给定模式 <code>pattern</code> 的 <code>key</code> 。</p>
<p><code>KEYS *</code> 匹配数据库中所有 <code>key</code> 。</p>
<p><code>KEYS h?llo</code> 匹配 <code>hello</code> ， <code>hallo</code> 和 <code>hxllo</code> 等。</p>
<p><code>KEYS h*llo</code> 匹配 <code>hllo</code> 和 <code>heeeeello</code> 等。</p>
<p><code>KEYS h[ae]llo</code> 匹配 <code>hello</code> 和 <code>hallo</code> ，但不匹配 <code>hillo</code> 。</p>
<p>特殊符号用 <code>\</code> 隔开</p>
<p>KEYS 的速度非常快，但在一个大的数据库中使用它仍然可能造成性能问题，如果你需要从一个数据集中查找特定的 <code>key</code> ，你最好还是用 Redis 的集合结构(set)来代替。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为数据库中 <code>key</code> 的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>符合给定模式的 <code>key</code> 列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; MSET one 1 two 2 three 3 four 4  # 一次设置 4 个 key</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; KEYS *o*</span><br><span class="line">1) &quot;four&quot;</span><br><span class="line">2) &quot;two&quot;</span><br><span class="line">3) &quot;one&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; KEYS t??</span><br><span class="line">1) &quot;two&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; KEYS t[w]*</span><br><span class="line">1) &quot;two&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; KEYS *  # 匹配数据库内所有 key</span><br><span class="line">1) &quot;four&quot;</span><br><span class="line">2) &quot;three&quot;</span><br><span class="line">3) &quot;two&quot;</span><br><span class="line">4) &quot;one&quot;</span><br></pre></td></tr></table></figure>

<h2 id="7-MIGRATE"><a href="#7-MIGRATE" class="headerlink" title="7.MIGRATE"></a>7.MIGRATE</h2><p><strong>MIGRATE host port key destination-db timeout [COPY] [REPLACE]</strong></p>
<p>将 <code>key</code> 原子性地从当前实例传送到目标实例的指定数据库上，一旦传送成功， <code>key</code> 保证会出现在目标实例上，而当前实例上的 <code>key</code> 会被删除。</p>
<p>这个命令是一个原子操作，它在执行的时候会阻塞进行迁移的两个实例，直到以下任意结果发生：迁移成功，迁移失败，等到超时。</p>
<p>命令的内部实现是这样的：它在当前实例对给定 <code>key</code> 执行 <em>DUMP<em>命令 ，将它序列化，然后传送到目标实例，目标实例再使用 *RESTORE</em>对数据进行反序列化，并将反序列化所得的数据添加到数据库中；当前实例就像目标实例的客户端那样，只要看到 *RESTORE</em> 命令返回 <code>OK</code> ，它就会调用 <em>DEL</em>删除自己数据库上的 <code>key</code> 。</p>
<p><code>timeout</code> 参数以毫秒为格式，指定当前实例和目标实例进行沟通的<strong>最大间隔时间</strong>。这说明操作并不一定要在 <code>timeout</code> 毫秒内完成，只是说数据传送的时间不能超过这个 <code>timeout</code> 数。</p>
<p>MIGRATE 命令需要在给定的时间规定内完成 IO 操作。如果在传送数据时发生 IO 错误，或者达到了超时时间，那么命令会停止执行，并返回一个特殊的错误： <code>IOERR</code> 。</p>
<p>当 <code>IOERR</code> 出现时，有以下两种可能：</p>
<ul>
<li><code>key</code> 可能存在于两个实例</li>
<li><code>key</code> 可能只存在于当前实例</li>
</ul>
<p>唯一不可能发生的情况就是丢失 <code>key</code> ，因此，如果一个客户端执行 MIGRATE命令，并且不幸遇上 <code>IOERR</code> 错误，那么这个客户端唯一要做的就是检查自己数据库上的 <code>key</code> 是否已经被正确地删除。</p>
<p>如果有其他错误发生，那么 MIGRATE 保证 <code>key</code> 只会出现在当前实例中。（当然，目标实例的给定数据库上可能有和 <code>key</code> 同名的键，不过这和 MIGRATE 命令没有关系）。</p>
<p><strong>可选项：</strong></p>
<ul>
<li><p><code>COPY</code> ：不移除源实例上的 <code>key</code> 。</p>
</li>
<li><p><code>REPLACE</code> ：替换目标实例上已存在的 <code>key</code> 。</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>这个命令在源实例上实际执行 <em>DUMP</em> 命令和 <em>DEL</em> 命令，在目标实例执行 <em>RESTORE</em> 命令，查看以上命令的文档可以看到详细的复杂度说明。<code>key</code> 数据在两个实例之间传输的复杂度为 O(N) 。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>迁移成功时返回 <code>OK</code> ，否则返回相应的错误。</p>
</li>
</ul>
<h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p>先启动两个 Redis 实例，一个使用默认的 6379 端口，一个使用 7777 端口。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-server &amp;</span><br><span class="line">1] 3557</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">$ .&#x2F;redis-server --port 7777 &amp;</span><br><span class="line">2] 3560</span><br><span class="line"></span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>然后用客户端连上 6379 端口的实例，设置一个键，然后将它迁移到 7777 端口的实例上：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-cli</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; flushdb</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET greeting &quot;Hello from 6379 instance&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; MIGRATE 127.0.0.1 7777 greeting 0 1000</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; EXISTS greeting                           # 迁移成功后 key 被删除</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<p>使用另一个客户端，查看 7777 端口上的实例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-cli -p 7777</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:7777&gt; GET greeting</span><br><span class="line">&quot;Hello from 6379 instance&quot;</span><br></pre></td></tr></table></figure>

<h2 id="8-MIGRATE"><a href="#8-MIGRATE" class="headerlink" title="8.MIGRATE"></a>8.MIGRATE</h2><p><strong>MIGRATE host port key destination-db timeout [COPY] [REPLACE]</strong></p>
<p>将 <code>key</code> 原子性地从当前实例传送到目标实例的指定数据库上，一旦传送成功， <code>key</code> 保证会出现在目标实例上，而当前实例上的 <code>key</code> 会被删除。</p>
<p>这个命令是一个原子操作，它在执行的时候会阻塞进行迁移的两个实例，直到以下任意结果发生：迁移成功，迁移失败，等到超时。</p>
<p>命令的内部实现是这样的：它在当前实例对给定 <code>key</code> 执行 <em>DUMP</em> 命令 ，将它序列化，然后传送到目标实例，目标实例再使用 <em>RESTORE</em> 对数据进行反序列化，并将反序列化所得的数据添加到数据库中；当前实例就像目标实例的客户端那样，只要看到 <em>RESTORE</em> 命令返回 <code>OK</code> ，它就会调用 <em>DEL</em> 删除自己数据库上的 <code>key</code> 。</p>
<p><code>timeout</code> 参数以毫秒为格式，指定当前实例和目标实例进行沟通的<strong>最大间隔时间</strong>。这说明操作并不一定要在 <code>timeout</code> 毫秒内完成，只是说数据传送的时间不能超过这个 <code>timeout</code> 数。</p>
<p>MIGRATE 命令需要在给定的时间规定内完成 IO 操作。如果在传送数据时发生 IO 错误，或者达到了超时时间，那么命令会停止执行，并返回一个特殊的错误： <code>IOERR</code> 。</p>
<p>当 <code>IOERR</code> 出现时，有以下两种可能：</p>
<ul>
<li><code>key</code> 可能存在于两个实例</li>
<li><code>key</code> 可能只存在于当前实例</li>
</ul>
<p>唯一不可能发生的情况就是丢失 <code>key</code> ，因此，如果一个客户端执行 MIGRATE 命令，并且不幸遇上 <code>IOERR</code> 错误，那么这个客户端唯一要做的就是检查自己数据库上的 <code>key</code> 是否已经被正确地删除。</p>
<p>如果有其他错误发生，那么 MIGRATE 保证 <code>key</code> 只会出现在当前实例中。（当然，目标实例的给定数据库上可能有和 <code>key</code> 同名的键，不过这和 MIGRATE 命令没有关系）。</p>
<p><strong>可选项：</strong></p>
<ul>
<li><p><code>COPY</code> ：不移除源实例上的 <code>key</code> 。</p>
</li>
<li><p><code>REPLACE</code> ：替换目标实例上已存在的 <code>key</code> 。</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>这个命令在源实例上实际执行 <em>DUMP</em> 命令和 <em>DEL</em> 命令，在目标实例执行 <em>RESTORE</em> 命令，查看以上命令的文档可以看到详细的复杂度说明。<code>key</code> 数据在两个实例之间传输的复杂度为 O(N) 。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>迁移成功时返回 <code>OK</code> ，否则返回相应的错误。</p>
</li>
</ul>
<h3 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a>示例</h3><p>先启动两个 Redis 实例，一个使用默认的 6379 端口，一个使用 7777 端口。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-server &amp;</span><br><span class="line">[1] 3557</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">$ .&#x2F;redis-server --port 7777 &amp;</span><br><span class="line">[2] 3560</span><br><span class="line"></span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>然后用客户端连上 6379 端口的实例，设置一个键，然后将它迁移到 7777 端口的实例上：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-cli</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; flushdb</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET greeting &quot;Hello from 6379 instance&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; MIGRATE 127.0.0.1 7777 greeting 0 1000</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; EXISTS greeting                           # 迁移成功后 key 被删除</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<p>使用另一个客户端，查看 7777 端口上的实例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-cli -p 7777</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:7777&gt; GET greeting</span><br><span class="line">&quot;Hello from 6379 instance&quot;</span><br></pre></td></tr></table></figure>

<h2 id="9-MOVE"><a href="#9-MOVE" class="headerlink" title="9.MOVE"></a>9.MOVE</h2><p><strong>MOVE key db</strong></p>
<p>将当前数据库的 <code>key</code> 移动到给定的数据库 <code>db</code> 当中。</p>
<p>如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 <code>key</code> ，或者 <code>key</code> 不存在于当前数据库，那么 <code>MOVE</code> 没有任何效果。</p>
<p>因此，也可以利用这一特性，将 MOVE 当作锁(locking)原语(primitive)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>移动成功返回 <code>1</code> ，失败则返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"># key 存在于当前数据库</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 0                             # redis默认使用数据库 0，为了清晰起见，这里再显式指定一次。</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET song &quot;secret base - Zone&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MOVE song 1                          # 将 song 移动到数据库 1</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS song                          # song 已经被移走</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 1                             # 使用数据库 1</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis:1&gt; EXISTS song                        # 证实 song 被移到了数据库 1 (注意命令提示符变成了&quot;redis:1&quot;，表明正在使用数据库 1)</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 当 key 不存在的时候</span><br><span class="line"></span><br><span class="line">redis:1&gt; EXISTS fake_key</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis:1&gt; MOVE fake_key 0                    # 试图从数据库 1 移动一个不存在的 key 到数据库 0，失败</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis:1&gt; select 0                           # 使用数据库0</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS fake_key                      # 证实 fake_key 不存在</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 当源数据库和目标数据库有相同的 key 时</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 0                             # 使用数据库0</span><br><span class="line">OK</span><br><span class="line">redis&gt; SET favorite_fruit &quot;banana&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 1                             # 使用数据库1</span><br><span class="line">OK</span><br><span class="line">redis:1&gt; SET favorite_fruit &quot;apple&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis:1&gt; SELECT 0                           # 使用数据库0，并试图将 favorite_fruit 移动到数据库 1</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MOVE favorite_fruit 1                # 因为两个数据库有相同的 key，MOVE 失败</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GET favorite_fruit                   # 数据库 0 的 favorite_fruit 没变</span><br><span class="line">&quot;banana&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 1</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis:1&gt; GET favorite_fruit                 # 数据库 1 的 favorite_fruit 也是</span><br><span class="line">&quot;apple&quot;</span><br></pre></td></tr></table></figure>

<h2 id="10-OBJECT"><a href="#10-OBJECT" class="headerlink" title="10.OBJECT"></a>10.OBJECT</h2><p><strong>OBJECT subcommand [arguments [arguments]]</strong></p>
<p>[OBJECT 命令允许从内部察看给定 <code>key</code> 的 Redis 对象。</p>
<p>它通常用在除错(debugging)或者了解为了节省空间而对 <code>key</code> 使用特殊编码的情况。</p>
<p>当将Redis用作缓存程序时，你也可以通过 [OBJECT 命令中的信息，决定 <code>key</code> 的驱逐策略(eviction policies)。</p>
<p>OBJECT 命令有多个子命令：</p>
<ul>
<li><code>OBJECT REFCOUNT &lt;key&gt;</code> 返回给定 <code>key</code> 引用所储存的值的次数。此命令主要用于除错。</li>
<li><code>OBJECT ENCODING &lt;key&gt;</code> 返回给定 <code>key</code> 锁储存的值所使用的内部表示(representation)。</li>
<li><code>OBJECT IDLETIME &lt;key&gt;</code> 返回给定 <code>key</code> 自储存以来的空转时间(idle， 没有被读取也没有被写入)，以秒为单位。</li>
</ul>
<p>对象可以以多种方式编码：</p>
<ul>
<li>字符串可以被编码为 <code>raw</code> (一般字符串)或 <code>int</code> (用字符串表示64位数字是为了节约空间)。</li>
<li>列表可以被编码为 <code>ziplist</code> 或 <code>linkedlist</code> 。 <code>ziplist</code> 是为节约大小较小的列表空间而作的特殊表示。</li>
<li>集合可以被编码为 <code>intset</code> 或者 <code>hashtable</code> 。 <code>intset</code> 是只储存数字的小集合的特殊表示。</li>
<li>哈希表可以编码为 <code>zipmap</code> 或者 <code>hashtable</code> 。 <code>zipmap</code> 是小哈希表的特殊表示。</li>
<li>有序集合可以被编码为 <code>ziplist</code> 或者 <code>skiplist</code> 格式。 <code>ziplist</code> 用于表示小的有序集合，而 <code>skiplist</code> 则用于表示任何大小的有序集合。</li>
</ul>
<p>假如你做了什么让 Redis 没办法再使用节省空间的编码时(比如将一个只有 1 个元素的集合扩展为一个有 100 万个元素的集合)，特殊编码类型(specially encoded types)会自动转换成通用类型(general type)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.3</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p><code>REFCOUNT</code> 和 <code>IDLETIME</code> 返回数字。<code>ENCODING</code> 返回相应的编码类型。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET game &quot;COD&quot;           # 设置一个字符串</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT REFCOUNT game     # 只有一个引用</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT IDLETIME game     # 等待一阵。。。然后查看空转时间</span><br><span class="line">(integer) 90</span><br><span class="line"></span><br><span class="line">redis&gt; GET game                 # 提取game， 让它处于活跃(active)状态</span><br><span class="line">&quot;COD&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT IDLETIME game     # 不再处于空转</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT ENCODING game     # 字符串的编码方式</span><br><span class="line">&quot;raw&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SET phone 15820123123    # 大的数字也被编码为字符串</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT ENCODING phone</span><br><span class="line">&quot;raw&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SET age 20               # 短数字被编码为 int</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; OBJECT ENCODING age</span><br><span class="line">&quot;int&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-PERSIST"><a href="#11-PERSIST" class="headerlink" title="11.PERSIST"></a>11.PERSIST</h2><p><strong>PERSIST key</strong></p>
<p>移除给定 <code>key</code> 的生存时间，将这个 <code>key</code> 从『易失的』(带生存时间 <code>key</code> )转换成『持久的』(一个不带生存时间、永不过期的 <code>key</code> )。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当生存时间移除成功时，返回 <code>1</code> .如果 <code>key</code> 不存在或 <code>key</code> 没有设置生存时间，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET mykey &quot;Hello&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXPIRE mykey 10  # 为 key 设置生存时间</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL mykey</span><br><span class="line">(integer) 10</span><br><span class="line"></span><br><span class="line">redis&gt; PERSIST mykey    # 移除 key 的生存时间</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL mykey</span><br><span class="line">(integer) -1</span><br></pre></td></tr></table></figure>

<h2 id="12-PEXPIRE"><a href="#12-PEXPIRE" class="headerlink" title="12.PEXPIRE"></a>12.PEXPIRE</h2><p><strong>PEXPIRE key milliseconds</strong></p>
<p>这个命令和 <em>EXPIRE</em> 命令的作用类似，但是它以毫秒为单位设置 <code>key</code> 的生存时间，而不像 <em>EXPIRE</em> 命令那样，以秒为单位。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功，返回 <code>1``key</code> 不存在或设置失败，返回 <code>0</code></p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET mykey &quot;Hello&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PEXPIRE mykey 1500</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL mykey    # TTL 的返回值以秒为单位</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL mykey   # PTTL 可以给出准确的毫秒数</span><br><span class="line">(integer) 1499</span><br></pre></td></tr></table></figure>

<h2 id="13-PEXPIREAT"><a href="#13-PEXPIREAT" class="headerlink" title="13.PEXPIREAT"></a>13.PEXPIREAT</h2><p><strong>PEXPIREAT key milliseconds-timestamp</strong></p>
<p>这个命令和 <em>EXPIREAT</em> 命令类似，但它以毫秒为单位设置 <code>key</code> 的过期 unix 时间戳，而不是像 <em>EXPIREAT</em> 那样，以秒为单位。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果生存时间设置成功，返回 <code>1</code> 。当 <code>key</code> 不存在或没办法设置生存时间时，返回 <code>0</code> 。(查看 <em>EXPIRE</em> 命令获取更多信息)</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET mykey &quot;Hello&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PEXPIREAT mykey 1555555555005</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL mykey           # TTL 返回秒</span><br><span class="line">(integer) 223157079</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL mykey          # PTTL 返回毫秒</span><br><span class="line">(integer) 223157079318</span><br></pre></td></tr></table></figure>

<h2 id="14-PTTL"><a href="#14-PTTL" class="headerlink" title="14.PTTL"></a>14.PTTL</h2><p><strong>PTTL key</strong></p>
<p>这个命令类似于 <em>TTL</em> 命令，但它以毫秒为单位返回 <code>key</code> 的剩余生存时间，而不是像 <em>TTL</em> 命令那样，以秒为单位。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当 <code>key</code> 不存在时，返回 <code>-2</code> 。当 <code>key</code> 存在但没有设置剩余生存时间时，返回 <code>-1</code> 。否则，以毫秒为单位，返回 <code>key</code> 的剩余生存时间。</p>
</li>
</ul>
<p>在 Redis 2.8 以前，当 <code>key</code> 不存在，或者 <code>key</code> 没有设置剩余生存时间时，命令都返回 <code>-1</code> 。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 不存在的 key</span><br><span class="line"></span><br><span class="line">redis&gt; FLUSHDB</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL key</span><br><span class="line">(integer) -2</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 存在，但没有设置剩余生存时间</span><br><span class="line"></span><br><span class="line">redis&gt; SET key value</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL key</span><br><span class="line">(integer) -1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 有剩余生存时间的 key</span><br><span class="line"></span><br><span class="line">redis&gt; PEXPIRE key 10086</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL key</span><br><span class="line">(integer) 6179</span><br></pre></td></tr></table></figure>

<h2 id="15-RANDOMKEY"><a href="#15-RANDOMKEY" class="headerlink" title="15.RANDOMKEY"></a>15.RANDOMKEY</h2><p><strong>RANDOMKEY</strong></p>
<p>从当前数据库中随机返回(不删除)一个 <code>key</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当数据库不为空时，返回一个 <code>key</code> 。当数据库为空时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"># 数据库不为空</span><br><span class="line"></span><br><span class="line">redis&gt; MSET fruit &quot;apple&quot; drink &quot;beer&quot; food &quot;cookies&quot;   # 设置多个 key</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; RANDOMKEY</span><br><span class="line">&quot;fruit&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RANDOMKEY</span><br><span class="line">&quot;food&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; KEYS *    # 查看数据库内所有key，证明 RANDOMKEY 并不删除 key</span><br><span class="line">1) &quot;food&quot;</span><br><span class="line">2) &quot;drink&quot;</span><br><span class="line">3) &quot;fruit&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 数据库为空</span><br><span class="line"></span><br><span class="line">redis&gt; FLUSHDB  # 删除当前数据库所有 key</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; RANDOMKEY</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="16-RENAME"><a href="#16-RENAME" class="headerlink" title="16.RENAME"></a>16.RENAME</h2><p><strong>RENAME key newkey</strong></p>
<p>将 <code>key</code> 改名为 <code>newkey</code> 。</p>
<p>当 <code>key</code> 和 <code>newkey</code> 相同，或者 <code>key</code> 不存在时，返回一个错误。</p>
<p>当 <code>newkey</code> 已经存在时， RENAME 命令将覆盖旧值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>改名成功时提示 <code>OK</code> ，失败时候返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"># key 存在且 newkey 不存在</span><br><span class="line"></span><br><span class="line">redis&gt; SET message &quot;hello world&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; RENAME message greeting</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS message               # message 不复存在</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS greeting              # greeting 取而代之</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 当 key 不存在时，返回错误</span><br><span class="line"></span><br><span class="line">redis&gt; RENAME fake_key never_exists</span><br><span class="line">(error) ERR no such key</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># newkey 已存在时， RENAME 会覆盖旧 newkey</span><br><span class="line"></span><br><span class="line">redis&gt; SET pc &quot;lenovo&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET personal_computer &quot;dell&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; RENAME pc personal_computer</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET pc</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line">redis:1&gt; GET personal_computer      # 原来的值 dell 被覆盖了</span><br><span class="line">&quot;lenovo&quot;</span><br></pre></td></tr></table></figure>

<h2 id="17-RENAMENX"><a href="#17-RENAMENX" class="headerlink" title="17.RENAMENX"></a>17.RENAMENX</h2><p><strong>RENAMENX key newkey</strong></p>
<p>当且仅当 <code>newkey</code> 不存在时，将 <code>key</code> 改名为 <code>newkey</code> 。</p>
<p>当 <code>key</code> 不存在时，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>修改成功时，返回 <code>1</code> 。如果 <code>newkey</code> 已经存在，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"># newkey 不存在，改名成功</span><br><span class="line"></span><br><span class="line">redis&gt; SET player &quot;MPlyaer&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS best_player</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; RENAMENX player best_player</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># newkey存在时，失败</span><br><span class="line"></span><br><span class="line">redis&gt; SET animal &quot;bear&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET favorite_animal &quot;butterfly&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; RENAMENX animal favorite_animal</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; get animal</span><br><span class="line">&quot;bear&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; get favorite_animal</span><br><span class="line">&quot;butterfly&quot;</span><br></pre></td></tr></table></figure>

<h2 id="18-RESTORE"><a href="#18-RESTORE" class="headerlink" title="18.RESTORE"></a>18.RESTORE</h2><p><strong>RESTORE key ttl serialized-value</strong></p>
<p>反序列化给定的序列化值，并将它和给定的 <code>key</code> 关联。</p>
<p>参数 <code>ttl</code> 以毫秒为单位为 <code>key</code> 设置生存时间；如果 <code>ttl</code> 为 <code>0</code> ，那么不设置生存时间。</p>
<p>[RESTORE 在执行反序列化之前会先对序列化值的 RDB 版本和数据校验和进行检查，如果 RDB 版本不相同或者数据不完整的话，那么 RESTORE 会拒绝进行反序列化，并返回一个错误。</p>
<p>更多信息可以参考 <em>DUMP</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>查找给定键的复杂度为 O(1) ，对键进行反序列化的复杂度为 O(N<em>M) ，其中 N 是构成 <code>key</code> 的 Redis 对象的数量，而 M 则是这些对象的平均大小。有序集合(sorted set)的反序列化复杂度为 O(N</em>M*log(N)) ，因为有序集合每次插入的复杂度为 O(log(N)) 。如果反序列化的对象是比较小的字符串，那么复杂度为 O(1) 。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果反序列化成功那么返回 <code>OK</code> ，否则返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET greeting &quot;hello, dumping world!&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DUMP greeting</span><br><span class="line">&quot;\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RESTORE greeting-again 0 &quot;\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET greeting-again</span><br><span class="line">&quot;hello, dumping world!&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RESTORE fake-message 0 &quot;hello moto moto blah blah&quot;   ; 使用错误的值进行反序列化</span><br><span class="line">(error) ERR DUMP payload version or checksum are wrong</span><br></pre></td></tr></table></figure>

<h2 id="19-SORT"><a href="#19-SORT" class="headerlink" title="19.SORT"></a>19.SORT</h2><p><strong>SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination]</strong></p>
<p>返回或保存给定列表、集合、有序集合 <code>key</code> 中经过排序的元素。</p>
<p>排序默认以数字作为对象，值被解释为双精度浮点数，然后进行比较。</p>
<h3 id="一般-SORT-用法"><a href="#一般-SORT-用法" class="headerlink" title="一般 SORT 用法"></a>一般 SORT 用法</h3><p>最简单的 SORT 使用方法是 <code>SORT key</code> 和 <code>SORT key DESC</code> ：</p>
<ul>
<li><code>SORT key</code> 返回键值从小到大排序的结果。</li>
<li><code>SORT key DESC</code> 返回键值从大到小排序的结果。</li>
</ul>
<p>假设 <code>today_cost</code> 列表保存了今日的开销金额， 那么可以用 SORT 命令对它进行排序：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># 开销金额列表</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH today_cost 30 1.5 10 8</span><br><span class="line">(integer) 4</span><br><span class="line"></span><br><span class="line"># 排序</span><br><span class="line"></span><br><span class="line">redis&gt; SORT today_cost</span><br><span class="line">1) &quot;1.5&quot;</span><br><span class="line">2) &quot;8&quot;</span><br><span class="line">3) &quot;10&quot;</span><br><span class="line">4) &quot;30&quot;</span><br><span class="line"></span><br><span class="line"># 逆序排序</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SORT today_cost DESC</span><br><span class="line">1) &quot;30&quot;</span><br><span class="line">2) &quot;10&quot;</span><br><span class="line">3) &quot;8&quot;</span><br><span class="line">4) &quot;1.5&quot;</span><br></pre></td></tr></table></figure>

<h3 id="使用-ALPHA-修饰符对字符串进行排序"><a href="#使用-ALPHA-修饰符对字符串进行排序" class="headerlink" title="使用 ALPHA 修饰符对字符串进行排序"></a>使用 ALPHA 修饰符对字符串进行排序</h3><p>因为 SORT 命令默认排序对象为数字， 当需要对字符串进行排序时， 需要显式地在 SORT 命令之后添加 <code>ALPHA</code> 修饰符：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"># 网址</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH website &quot;www.reddit.com&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH website &quot;www.slashdot.com&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH website &quot;www.infoq.com&quot;</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line"># 默认（按数字）排序</span><br><span class="line"></span><br><span class="line">redis&gt; SORT website</span><br><span class="line">1) &quot;www.infoq.com&quot;</span><br><span class="line">2) &quot;www.slashdot.com&quot;</span><br><span class="line">3) &quot;www.reddit.com&quot;</span><br><span class="line"></span><br><span class="line"># 按字符排序</span><br><span class="line"></span><br><span class="line">redis&gt; SORT website ALPHA</span><br><span class="line">1) &quot;www.infoq.com&quot;</span><br><span class="line">2) &quot;www.reddit.com&quot;</span><br><span class="line">3) &quot;www.slashdot.com&quot;</span><br></pre></td></tr></table></figure>

<p>如果系统正确地设置了 <code>LC_COLLATE</code> 环境变量的话，Redis能识别 <code>UTF-8</code> 编码。</p>
<h3 id="使用-LIMIT-修饰符限制返回结果"><a href="#使用-LIMIT-修饰符限制返回结果" class="headerlink" title="使用 LIMIT 修饰符限制返回结果"></a>使用 LIMIT 修饰符限制返回结果</h3><p>排序之后返回元素的数量可以通过 <code>LIMIT</code> 修饰符进行限制， 修饰符接受 <code>offset</code> 和 <code>count</code> 两个参数：</p>
<ul>
<li><code>offset</code> 指定要跳过的元素数量。</li>
<li><code>count</code> 指定跳过 <code>offset</code> 个指定的元素之后，要返回多少个对象。</li>
</ul>
<p>以下例子返回排序结果的前 5 个对象( <code>offset</code> 为 <code>0</code> 表示没有元素被跳过)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 添加测试数据，列表值为 1 指 10</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; RPUSH rank 1 3 5 7 9</span><br><span class="line">(integer) 5</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; RPUSH rank 2 4 6 8 10</span><br><span class="line">(integer) 10</span><br><span class="line"></span><br><span class="line"># 返回列表中最小的 5 个值</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SORT rank LIMIT 0 5</span><br><span class="line">1) &quot;1&quot;</span><br><span class="line">2) &quot;2&quot;</span><br><span class="line">3) &quot;3&quot;</span><br><span class="line">4) &quot;4&quot;</span><br><span class="line">5) &quot;5&quot;</span><br></pre></td></tr></table></figure>

<p>可以组合使用多个修饰符。以下例子返回从大到小排序的前 5 个对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT rank LIMIT 0 5 DESC</span><br><span class="line">1) &quot;10&quot;</span><br><span class="line">2) &quot;9&quot;</span><br><span class="line">3) &quot;8&quot;</span><br><span class="line">4) &quot;7&quot;</span><br><span class="line">5) &quot;6&quot;</span><br></pre></td></tr></table></figure>

<h3 id="使用外部-key-进行排序"><a href="#使用外部-key-进行排序" class="headerlink" title="使用外部 key 进行排序"></a>使用外部 key 进行排序</h3><p>可以使用外部 <code>key</code> 的数据作为权重，代替默认的直接对比键值的方式来进行排序。</p>
<p>假设现在有用户数据如下：</p>
<table>
<thead>
<tr>
<th align="left">uid</th>
<th align="left">user_name_{uid}</th>
<th align="left">user_level_{uid}</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left">admin</td>
<td align="left">9999</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">jack</td>
<td align="left">10</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">peter</td>
<td align="left">25</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">mary</td>
<td align="left">70</td>
</tr>
</tbody></table>
<p>以下代码将数据输入到 Redis 中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"># admin</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LPUSH uid 1</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_name_1 admin</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_level_1 9999</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># jack</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LPUSH uid 2</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_name_2 jack</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_level_2 10</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># peter</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LPUSH uid 3</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_name_3 peter</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_level_3 25</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># mary</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LPUSH uid 4</span><br><span class="line">(integer) 4</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_name_4 mary</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET user_level_4 70</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h4 id="BY-选项"><a href="#BY-选项" class="headerlink" title="BY 选项"></a>BY 选项</h4><p>默认情况下， <code>SORT uid</code> 直接按 <code>uid</code> 中的值排序：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid</span><br><span class="line">1) &quot;1&quot;      # admin</span><br><span class="line">2) &quot;2&quot;      # jack</span><br><span class="line">3) &quot;3&quot;      # peter</span><br><span class="line">4) &quot;4&quot;      # mary</span><br></pre></td></tr></table></figure>

<p>通过使用 <code>BY</code> 选项，可以让 <code>uid</code> 按其他键的元素来排序。</p>
<p>比如说， 以下代码让 <code>uid</code> 键按照 <code>user_level_{uid}</code> 的大小来排序：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY user_level_*</span><br><span class="line">1) &quot;2&quot;      # jack , level &#x3D; 10</span><br><span class="line">2) &quot;3&quot;      # peter, level &#x3D; 25</span><br><span class="line">3) &quot;4&quot;      # mary, level &#x3D; 70</span><br><span class="line">4) &quot;1&quot;      # admin, level &#x3D; 9999</span><br></pre></td></tr></table></figure>

<p><code>user_level_*</code> 是一个占位符， 它先取出 <code>uid</code> 中的值， 然后再用这个值来查找相应的键。</p>
<p>比如在对 <code>uid</code> 列表进行排序时， 程序就会先取出 <code>uid</code> 的值 <code>1</code> 、 <code>2</code> 、 <code>3</code> 、 <code>4</code> ， 然后使用 <code>user_level_1</code> 、 <code>user_level_2</code> 、 <code>user_level_3</code> 和 <code>user_level_4</code> 的值作为排序 <code>uid</code> 的权重。</p>
<h4 id="GET-选项"><a href="#GET-选项" class="headerlink" title="GET 选项"></a>GET 选项</h4><p>使用 <code>GET</code> 选项， 可以根据排序的结果来取出相应的键值。</p>
<p>比如说， 以下代码先排序 <code>uid</code> ， 再取出键 <code>user_name_{uid}</code> 的值：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid GET user_name_*</span><br><span class="line">1) &quot;admin&quot;</span><br><span class="line">2) &quot;jack&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line">4) &quot;mary&quot;</span><br></pre></td></tr></table></figure>

<h4 id="组合使用-BY-和-GET"><a href="#组合使用-BY-和-GET" class="headerlink" title="组合使用 BY 和 GET"></a>组合使用 BY 和 GET</h4><p>通过组合使用 <code>BY</code> 和 <code>GET</code> ， 可以让排序结果以更直观的方式显示出来。</p>
<p>比如说， 以下代码先按 <code>user_level_{uid}</code> 来排序 <code>uid</code> 列表， 再取出相应的 <code>user_name_{uid}</code> 的值：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY user_level_* GET user_name_*</span><br><span class="line">1) &quot;jack&quot;       # level &#x3D; 10</span><br><span class="line">2) &quot;peter&quot;      # level &#x3D; 25</span><br><span class="line">3) &quot;mary&quot;       # level &#x3D; 70</span><br><span class="line">4) &quot;admin&quot;      # level &#x3D; 9999</span><br></pre></td></tr></table></figure>

<p>现在的排序结果要比只使用 <code>SORT uid BY user_level_*</code> 要直观得多。</p>
<h4 id="获取多个外部键"><a href="#获取多个外部键" class="headerlink" title="获取多个外部键"></a>获取多个外部键</h4><p>可以同时使用多个 <code>GET</code> 选项， 获取多个外部键的值。</p>
<p>以下代码就按 <code>uid</code> 分别获取 <code>user_level_{uid}</code> 和 <code>user_name_{uid}</code> ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid GET user_level_* GET user_name_*</span><br><span class="line">1) &quot;9999&quot;       # level</span><br><span class="line">2) &quot;admin&quot;      # name</span><br><span class="line">3) &quot;10&quot;</span><br><span class="line">4) &quot;jack&quot;</span><br><span class="line">5) &quot;25&quot;</span><br><span class="line">6) &quot;peter&quot;</span><br><span class="line">7) &quot;70&quot;</span><br><span class="line">8) &quot;mary&quot;</span><br></pre></td></tr></table></figure>

<p><code>GET</code> 有一个额外的参数规则，那就是 —— 可以用 <code>#</code> 获取被排序键的值。</p>
<p>以下代码就将 <code>uid</code> 的值、及其相应的 <code>user_level_*</code> 和 <code>user_name_*</code> 都返回为结果：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid GET # GET user_level_* GET user_name_*</span><br><span class="line">1) &quot;1&quot;          # uid</span><br><span class="line">2) &quot;9999&quot;       # level</span><br><span class="line">3) &quot;admin&quot;      # name</span><br><span class="line">4) &quot;2&quot;</span><br><span class="line">5) &quot;10&quot;</span><br><span class="line">6) &quot;jack&quot;</span><br><span class="line">7) &quot;3&quot;</span><br><span class="line">8) &quot;25&quot;</span><br><span class="line">9) &quot;peter&quot;</span><br><span class="line">10) &quot;4&quot;</span><br><span class="line">11) &quot;70&quot;</span><br><span class="line">12) &quot;mary&quot;</span><br></pre></td></tr></table></figure>

<h4 id="获取外部键，但不进行排序"><a href="#获取外部键，但不进行排序" class="headerlink" title="获取外部键，但不进行排序"></a>获取外部键，但不进行排序</h4><p>通过将一个不存在的键作为参数传给 <code>BY</code> 选项， 可以让 <code>SORT</code> 跳过排序操作， 直接返回结果：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY not-exists-key</span><br><span class="line">1) &quot;4&quot;</span><br><span class="line">2) &quot;3&quot;</span><br><span class="line">3) &quot;2&quot;</span><br><span class="line">4) &quot;1&quot;</span><br></pre></td></tr></table></figure>

<p>这种用法在单独使用时，没什么实际用处。</p>
<p>不过，通过将这种用法和 <code>GET</code> 选项配合， 就可以在不排序的情况下， 获取多个外部键， 相当于执行一个整合的获取操作（类似于 SQL 数据库的 <code>join</code> 关键字）。</p>
<p>以下代码演示了，如何在不引起排序的情况下，使用 <code>SORT</code> 、 <code>BY</code> 和 <code>GET</code> 获取多个外部键：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY not-exists-key GET # GET user_level_* GET user_name_*</span><br><span class="line">1) &quot;4&quot;      # id</span><br><span class="line">2) &quot;70&quot;     # level</span><br><span class="line">3) &quot;mary&quot;   # name</span><br><span class="line">4) &quot;3&quot;</span><br><span class="line">5) &quot;25&quot;</span><br><span class="line">6) &quot;peter&quot;</span><br><span class="line">7) &quot;2&quot;</span><br><span class="line">8) &quot;10&quot;</span><br><span class="line">9) &quot;jack&quot;</span><br><span class="line">10) &quot;1&quot;</span><br><span class="line">11) &quot;9999&quot;</span><br><span class="line">12) &quot;admin&quot;</span><br></pre></td></tr></table></figure>

<h4 id="将哈希表作为-GET-或-BY-的参数"><a href="#将哈希表作为-GET-或-BY-的参数" class="headerlink" title="将哈希表作为 GET 或 BY 的参数"></a>将哈希表作为 GET 或 BY 的参数</h4><p>除了可以将字符串键之外， 哈希表也可以作为 <code>GET</code> 或 <code>BY</code> 选项的参数来使用。</p>
<p>比如说，对于前面给出的用户信息表：</p>
<table>
<thead>
<tr>
<th align="left">uid</th>
<th align="left">user_name_{uid}</th>
<th align="left">user_level_{uid}</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left">admin</td>
<td align="left">9999</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">jack</td>
<td align="left">10</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">peter</td>
<td align="left">25</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">mary</td>
<td align="left">70</td>
</tr>
</tbody></table>
<p>我们可以不将用户的名字和级别保存在 <code>user_name_{uid}</code> 和 <code>user_level_{uid}</code> 两个字符串键中， 而是用一个带有 <code>name</code> 域和 <code>level</code> 域的哈希表 <code>user_info_{uid}</code> 来保存用户的名字和级别信息：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; HMSET user_info_1 name admin level 9999</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; HMSET user_info_2 name jack level 10</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; HMSET user_info_3 name peter level 25</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; HMSET user_info_4 name mary level 70</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>之后， <code>BY</code> 和 <code>GET</code> 选项都可以用 <code>key-&gt;field</code> 的格式来获取哈希表中的域的值， 其中 <code>key</code> 表示哈希表键， 而 <code>field</code> 则表示哈希表的域：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY user_info_*-&gt;level</span><br><span class="line">1) &quot;2&quot;</span><br><span class="line">2) &quot;3&quot;</span><br><span class="line">3) &quot;4&quot;</span><br><span class="line">4) &quot;1&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SORT uid BY user_info_*-&gt;level GET user_info_*-&gt;name</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;peter&quot;</span><br><span class="line">3) &quot;mary&quot;</span><br><span class="line">4) &quot;admin&quot;</span><br></pre></td></tr></table></figure>

<h3 id="保存排序结果"><a href="#保存排序结果" class="headerlink" title="保存排序结果"></a>保存排序结果</h3><p>默认情况下， [SORT 操作只是简单地返回排序结果，并不进行任何保存操作。</p>
<p>通过给 <code>STORE</code> 选项指定一个 <code>key</code> 参数，可以将排序结果保存到给定的键上。</p>
<p>如果被指定的 <code>key</code> 已存在，那么原有的值将被排序结果覆盖。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"># 测试数据</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; RPUSH numbers 1 3 5 7 9</span><br><span class="line">(integer) 5</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; RPUSH numbers 2 4 6 8 10</span><br><span class="line">(integer) 10</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LRANGE numbers 0 -1</span><br><span class="line">1) &quot;1&quot;</span><br><span class="line">2) &quot;3&quot;</span><br><span class="line">3) &quot;5&quot;</span><br><span class="line">4) &quot;7&quot;</span><br><span class="line">5) &quot;9&quot;</span><br><span class="line">6) &quot;2&quot;</span><br><span class="line">7) &quot;4&quot;</span><br><span class="line">8) &quot;6&quot;</span><br><span class="line">9) &quot;8&quot;</span><br><span class="line">10) &quot;10&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SORT numbers STORE sorted-numbers</span><br><span class="line">(integer) 10</span><br><span class="line"></span><br><span class="line"># 排序后的结果</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; LRANGE sorted-numbers 0 -1</span><br><span class="line">1) &quot;1&quot;</span><br><span class="line">2) &quot;2&quot;</span><br><span class="line">3) &quot;3&quot;</span><br><span class="line">4) &quot;4&quot;</span><br><span class="line">5) &quot;5&quot;</span><br><span class="line">6) &quot;6&quot;</span><br><span class="line">7) &quot;7&quot;</span><br><span class="line">8) &quot;8&quot;</span><br><span class="line">9) &quot;9&quot;</span><br><span class="line">10) &quot;10&quot;</span><br></pre></td></tr></table></figure>

<p>可以通过将 SORT 命令的执行结果保存，并用 <em>EXPIRE</em> 为结果设置生存时间，以此来产生一个 SORT 操作的结果缓存。</p>
<p>这样就可以避免对 SORT 操作的频繁调用：只有当结果集过期时，才需要再调用一次 SORT 操作。</p>
<p>另外，为了正确实现这一用法，你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端，同一时间进行 SORT 操作，并保存为结果集)，具体参见 <em>SETNX</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N+M*log(M))， <code>N</code> 为要排序的列表或集合内的元素数量， <code>M</code> 为要返回的元素数量。如果只是使用 SORT 命令的 <code>GET</code> 选项获取数据而没有进行排序，时间复杂度 O(N)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>没有使用 <code>STORE</code> 参数，返回列表形式的排序结果。使用 <code>STORE</code> 参数，返回排序结果的元素数量。</p>
</li>
</ul>
<h2 id="20-TTL"><a href="#20-TTL" class="headerlink" title="20.TTL"></a>20.TTL</h2><p><strong>TTL key</strong></p>
<p>以秒为单位，返回给定 <code>key</code> 的剩余生存时间(TTL, time to live)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当 <code>key</code> 不存在时，返回 <code>-2</code> 。当 <code>key</code> 存在但没有设置剩余生存时间时，返回 <code>-1</code> 。否则，以秒为单位，返回 <code>key</code> 的剩余生存时间。</p>
</li>
</ul>
<p>在 Redis 2.8 以前，当 <code>key</code> 不存在，或者 <code>key</code> 没有设置剩余生存时间时，命令都返回 <code>-1</code> 。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 不存在的 key</span><br><span class="line"></span><br><span class="line">redis&gt; FLUSHDB</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; TTL key</span><br><span class="line">(integer) -2</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 存在，但没有设置剩余生存时间</span><br><span class="line"></span><br><span class="line">redis&gt; SET key value</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; TTL key</span><br><span class="line">(integer) -1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 有剩余生存时间的 key</span><br><span class="line"></span><br><span class="line">redis&gt; EXPIRE key 10086</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TTL key</span><br><span class="line">(integer) 10084</span><br></pre></td></tr></table></figure>

<h2 id="21-TYPE"><a href="#21-TYPE" class="headerlink" title="21.TYPE"></a>21.TYPE</h2><p><strong>TYPE key</strong></p>
<p>返回 <code>key</code> 所储存的值的类型。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p><code>none</code> (key不存在)<code>string</code> (字符串)<code>list</code> (列表)<code>set</code> (集合)<code>zset</code> (有序集)<code>hash</code> (哈希表)</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 字符串</span><br><span class="line"></span><br><span class="line">redis&gt; SET weather &quot;sunny&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; TYPE weather</span><br><span class="line">string</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 列表</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH book_list &quot;programming in scala&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TYPE book_list</span><br><span class="line">list</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 集合</span><br><span class="line"></span><br><span class="line">redis&gt; SADD pat &quot;dog&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; TYPE pat</span><br><span class="line">set</span><br></pre></td></tr></table></figure>

<h2 id="22-SCAN"><a href="#22-SCAN" class="headerlink" title="22.SCAN"></a>22.SCAN</h2><p><strong>SCAN cursor [MATCH pattern] [COUNT count]</strong></p>
<p><em>SCAN</em> 命令及其相关的 <em>SSCAN</em> 命令、 <em>HSCAN</em> 命令和 <em>ZSCAN</em> 命令都用于增量地迭代（incrementally iterate）一集元素（a collection of elements）：</p>
<ul>
<li><em>SCAN</em> 命令用于迭代当前数据库中的数据库键。</li>
<li><em>SSCAN</em> 命令用于迭代集合键中的元素。</li>
<li><em>HSCAN</em> 命令用于迭代哈希键中的键值对。</li>
<li><em>ZSCAN</em> 命令用于迭代有序集合中的元素（包括元素成员和元素分值）。</li>
</ul>
<p>以上列出的四个命令都支持增量式迭代， 它们每次执行都只会返回少量元素， 所以这些命令可以用于生产环境， 而不会出现像 <em>KEYS</em> 命令、 <em>SMEMBERS</em> 命令带来的问题 —— 当 <em>KEYS</em> 命令被用于处理一个大的数据库时， 又或者 <em>SMEMBERS</em> 命令被用于处理一个大的集合键时， 它们可能会阻塞服务器达数秒之久。</p>
<p>不过， 增量式迭代命令也不是没有缺点的： 举个例子， 使用 <em>SMEMBERS</em> 命令可以返回集合键当前包含的所有元素， 但是对于 <em>SCAN</em> 这类增量式迭代命令来说， 因为在对键进行增量式迭代的过程中， 键可能会被修改， 所以增量式迭代命令只能对被返回的元素提供有限的保证 （offer limited guarantees about the returned elements）。</p>
<p>因为 <em>SCAN</em> 、 <em>SSCAN</em> 、 <em>HSCAN</em> 和 <em>ZSCAN</em> 四个命令的工作方式都非常相似， 所以这个文档会一并介绍这四个命令， 但是要记住：</p>
<ul>
<li><em>SSCAN</em> 命令、 <em>HSCAN</em> 命令和 <em>ZSCAN</em> 命令的第一个参数总是一个数据库键。</li>
<li>而 <em>SCAN</em> 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。</li>
</ul>
<h3 id="SCAN-命令的基本用法"><a href="#SCAN-命令的基本用法" class="headerlink" title="SCAN 命令的基本用法"></a>SCAN 命令的基本用法</h3><p><em>SCAN</em> 命令是一个基于游标的迭代器（cursor based iterator）： <em>SCAN</em> 命令每次被调用之后， 都会向用户返回一个新的游标， 用户在下次迭代时需要使用这个新游标作为 <em>SCAN</em> 命令的游标参数， 以此来延续之前的迭代过程。</p>
<p>当 <em>SCAN</em> 命令的游标参数被设置为 <code>0</code> 时， 服务器将开始一次新的迭代， 而当服务器向用户返回值为 <code>0</code> 的游标时， 表示迭代已结束。</p>
<p>以下是一个 <em>SCAN</em> 命令的迭代过程示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; scan 0</span><br><span class="line">1) &quot;17&quot;</span><br><span class="line">2)  1) &quot;key:12&quot;</span><br><span class="line">    2) &quot;key:8&quot;</span><br><span class="line">    3) &quot;key:4&quot;</span><br><span class="line">    4) &quot;key:14&quot;</span><br><span class="line">    5) &quot;key:16&quot;</span><br><span class="line">    6) &quot;key:17&quot;</span><br><span class="line">    7) &quot;key:15&quot;</span><br><span class="line">    8) &quot;key:10&quot;</span><br><span class="line">    9) &quot;key:3&quot;</span><br><span class="line">    10) &quot;key:7&quot;</span><br><span class="line">    11) &quot;key:1&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; scan 17</span><br><span class="line">1) &quot;0&quot;</span><br><span class="line">2) 1) &quot;key:5&quot;</span><br><span class="line">   2) &quot;key:18&quot;</span><br><span class="line">   3) &quot;key:0&quot;</span><br><span class="line">   4) &quot;key:2&quot;</span><br><span class="line">   5) &quot;key:19&quot;</span><br><span class="line">   6) &quot;key:13&quot;</span><br><span class="line">   7) &quot;key:6&quot;</span><br><span class="line">   8) &quot;key:9&quot;</span><br><span class="line">   9) &quot;key:11&quot;</span><br></pre></td></tr></table></figure>

<p>在上面这个例子中， 第一次迭代使用 <code>0</code> 作为游标， 表示开始一次新的迭代。</p>
<p>第二次迭代使用的是第一次迭代时返回的游标， 也即是命令回复第一个元素的值 —— <code>17</code> 。</p>
<p>从上面的示例可以看到， [<em>SCAN</em> 命令的回复是一个包含两个元素的数组， 第一个数组元素是用于进行下一次迭代的新游标， 而第二个数组元素则是一个数组， 这个数组中包含了所有被迭代的元素。</p>
<p>在第二次调用 [<em>SCAN</em> 命令时， 命令返回了游标 <code>0</code> ， 这表示迭代已经结束， 整个数据集（collection）已经被完整遍历过了。</p>
<p>以 <code>0</code> 作为游标开始一次新的迭代， 一直调用 [<em>SCAN</em> 命令， 直到命令返回游标 <code>0</code> ， 我们称这个过程为一次<strong>完整遍历</strong>（full iteration）。</p>
<h3 id="SCAN-命令的保证（guarantees）"><a href="#SCAN-命令的保证（guarantees）" class="headerlink" title="SCAN 命令的保证（guarantees）"></a>SCAN 命令的保证（guarantees）</h3><p>[<em>SCAN</em> 命令， 以及其他增量式迭代命令， 在进行完整遍历的情况下可以为用户带来以下保证： 从完整遍历开始直到完整遍历结束期间， 一直存在于数据集内的所有元素都会被完整遍历返回； 这意味着， 如果有一个元素， 它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中， 那么 [<em>SCAN</em> 命令总会在某次迭代中将这个元素返回给用户。</p>
<p>然而因为增量式命令仅仅使用游标来记录迭代状态， 所以这些命令带有以下缺点：</p>
<ul>
<li>同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责， 比如说， 可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。</li>
<li>如果一个元素是在迭代过程中被添加到数据集的， 又或者是在迭代过程中从数据集中被删除的， 那么这个元素可能会被返回， 也可能不会， 这是未定义的（undefined）。</li>
</ul>
<h3 id="SCAN-命令每次执行返回的元素数量"><a href="#SCAN-命令每次执行返回的元素数量" class="headerlink" title="SCAN 命令每次执行返回的元素数量"></a>SCAN 命令每次执行返回的元素数量</h3><p>增量式迭代命令并不保证每次执行都返回某个给定数量的元素。</p>
<p>增量式命令甚至可能会返回零个元素， 但只要命令返回的游标不是 <code>0</code> ， 应用程序就不应该将迭代视作结束。</p>
<p>不过命令返回的元素数量总是符合一定规则的， 在实际中：</p>
<ul>
<li>对于一个大数据集来说， 增量式迭代命令每次最多可能会返回数十个元素；</li>
<li>而对于一个足够小的数据集来说， 如果这个数据集的底层表示为编码数据结构（encoded data structure，适用于是小集合键、小哈希键和小有序集合键）， 那么增量迭代命令将在一次调用中返回数据集中的所有元素。</li>
</ul>
<p>最后， 用户可以通过增量式迭代命令提供的 <code>COUNT</code> 选项来指定每次迭代返回元素的最大值。</p>
<h3 id="COUNT-选项"><a href="#COUNT-选项" class="headerlink" title="COUNT 选项"></a>COUNT 选项</h3><p>虽然增量式迭代命令不保证每次迭代所返回的元素数量， 但我们可以使用 <code>COUNT</code> 选项， 对命令的行为进行一定程度上的调整。</p>
<p>基本上， <code>COUNT</code> 选项的作用就是让用户告知迭代命令， 在每次迭代中应该从数据集里返回多少元素。</p>
<p>虽然 <code>COUNT</code> 选项<strong>只是对增量式迭代命令的一种提示</strong>（hint）， 但是在大多数情况下， 这种提示都是有效的。</p>
<ul>
<li><code>COUNT</code> 参数的默认值为 <code>10</code> 。</li>
<li>在迭代一个足够大的、由哈希表实现的数据库、集合键、哈希键或者有序集合键时， 如果用户没有使用 <code>MATCH</code> 选项， 那么命令返回的元素数量通常和 <code>COUNT</code> 选项指定的一样， 或者比 <code>COUNT</code> 选项指定的数量稍多一些。</li>
<li>在迭代一个编码为整数集合（intset，一个只由整数值构成的小集合）、 或者编码为压缩列表（ziplist，由不同值构成的一个小哈希或者一个小有序集合）时， 增量式迭代命令通常会无视 <code>COUNT</code> 选项指定的值， 在第一次迭代就将数据集包含的所有元素都返回给用户。</li>
</ul>
<p><strong>并非每次迭代都要使用相同的</strong> <code>COUNT</code> <strong>值。</strong></p>
<p>用户可以在每次迭代中按自己的需要随意改变 <code>COUNT</code> 值， 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。</p>
<h3 id="MATCH-选项"><a href="#MATCH-选项" class="headerlink" title="MATCH 选项"></a>MATCH 选项</h3><p>和 [<em>KEYS</em> 命令一样， 增量式迭代命令也可以通过提供一个 glob 风格的模式参数， 让命令只返回和给定模式相匹配的元素， 这一点可以通过在执行增量式迭代命令时， 通过给定 <code>MATCH &lt;pattern&gt;</code> 参数来实现。</p>
<p>以下是一个使用 <code>MATCH</code> 选项进行迭代的示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; sadd myset 1 2 3 foo foobar feelsgood</span><br><span class="line">(integer) 6</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; sscan myset 0 match f*</span><br><span class="line">1) &quot;0&quot;</span><br><span class="line">2) 1) &quot;foo&quot;</span><br><span class="line">   2) &quot;feelsgood&quot;</span><br><span class="line">   3) &quot;foobar&quot;</span><br></pre></td></tr></table></figure>

<p>需要注意的是， 对元素的模式匹配工作是在命令从数据集中取出元素之后， 向客户端返回元素之前的这段时间内进行的， 所以如果被迭代的数据集中只有少量元素和模式相匹配， 那么迭代命令或许会在多次执行中都不返回任何元素。</p>
<p>以下是这种情况的一个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; scan 0 MATCH *11*</span><br><span class="line">1) &quot;288&quot;</span><br><span class="line">2) 1) &quot;key:911&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; scan 288 MATCH *11*</span><br><span class="line">1) &quot;224&quot;</span><br><span class="line">2) (empty list or set)</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; scan 224 MATCH *11*</span><br><span class="line">1) &quot;80&quot;</span><br><span class="line">2) (empty list or set)</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; scan 80 MATCH *11*</span><br><span class="line">1) &quot;176&quot;</span><br><span class="line">2) (empty list or set)</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; scan 176 MATCH *11* COUNT 1000</span><br><span class="line">1) &quot;0&quot;</span><br><span class="line">2)  1) &quot;key:611&quot;</span><br><span class="line">    2) &quot;key:711&quot;</span><br><span class="line">    3) &quot;key:118&quot;</span><br><span class="line">    4) &quot;key:117&quot;</span><br><span class="line">    5) &quot;key:311&quot;</span><br><span class="line">    6) &quot;key:112&quot;</span><br><span class="line">    7) &quot;key:111&quot;</span><br><span class="line">    8) &quot;key:110&quot;</span><br><span class="line">    9) &quot;key:113&quot;</span><br><span class="line">   10) &quot;key:211&quot;</span><br><span class="line">   11) &quot;key:411&quot;</span><br><span class="line">   12) &quot;key:115&quot;</span><br><span class="line">   13) &quot;key:116&quot;</span><br><span class="line">   14) &quot;key:114&quot;</span><br><span class="line">   15) &quot;key:119&quot;</span><br><span class="line">   16) &quot;key:811&quot;</span><br><span class="line">   17) &quot;key:511&quot;</span><br><span class="line">   18) &quot;key:11&quot;</span><br></pre></td></tr></table></figure>

<p>如你所见， 以上的大部分迭代都不返回任何元素。</p>
<p>在最后一次迭代， 我们通过将 <code>COUNT</code> 选项的参数设置为 <code>1000</code> ， 强制命令为本次迭代扫描更多元素， 从而使得命令返回的元素也变多了。</p>
<h3 id="并发执行多个迭代"><a href="#并发执行多个迭代" class="headerlink" title="并发执行多个迭代"></a>并发执行多个迭代</h3><p>在同一时间， 可以有任意多个客户端对同一数据集进行迭代， 客户端每次执行迭代都需要传入一个游标， 并在迭代执行之后获得一个新的游标， 而这个游标就包含了迭代的所有状态， 因此， 服务器无须为迭代记录任何状态。</p>
<h3 id="中途停止迭代"><a href="#中途停止迭代" class="headerlink" title="中途停止迭代"></a>中途停止迭代</h3><p>因为迭代的所有状态都保存在游标里面， 而服务器无须为迭代保存任何状态， 所以客户端可以在中途停止一个迭代， 而无须对服务器进行任何通知。</p>
<p>即使有任意数量的迭代在中途停止， 也不会产生任何问题。</p>
<h3 id="使用错误的游标进行增量式迭代"><a href="#使用错误的游标进行增量式迭代" class="headerlink" title="使用错误的游标进行增量式迭代"></a>使用错误的游标进行增量式迭代</h3><p>使用间断的（broken）、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃， 但可能会让命令产生未定义的行为。</p>
<p>未定义行为指的是， 增量式命令对返回值所做的保证可能会不再为真。</p>
<p>只有两种游标是合法的：</p>
<ol>
<li>在开始一个新的迭代时， 游标必须为 <code>0</code> 。</li>
<li>增量式迭代命令在执行之后返回的， 用于延续（continue）迭代过程的游标。</li>
</ol>
<h3 id="迭代终结的保证"><a href="#迭代终结的保证" class="headerlink" title="迭代终结的保证"></a>迭代终结的保证</h3><p>增量式迭代命令所使用的算法只保证在数据集的大小有界（bounded）的情况下， 迭代才会停止， 换句话说， 如果被迭代数据集的大小不断地增长的话， 增量式迭代命令可能永远也无法完成一次完整迭代。</p>
<p>从直觉上可以看出， 当一个数据集不断地变大时， 想要访问这个数据集中的所有元素就需要做越来越多的工作， 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。</p>
<p><strong>可用版本：</strong></p>
<blockquote>
<p>&gt;= 2.8.0</p>
</blockquote>
<p><strong>时间复杂度：</strong></p>
<blockquote>
<p>增量式迭代命令每次执行的复杂度为 O(1) ， 对数据集进行一次完整迭代的复杂度为 O(N) ， 其中 N 为数据集中的元素数量。</p>
</blockquote>
<p><strong>返回值：</strong></p>
<blockquote>
<p><em>SCAN</em> 命令、 [<em>SSCAN</em> 命令、 [<em>HSCAN</em> 命令和 [<em>ZSCAN</em> 命令都返回一个包含两个元素的 multi-bulk 回复： 回复的第一个元素是字符串表示的无符号 64 位整数（游标）， 回复的第二个元素是另一个 multi-bulk 回复， 这个 multi-bulk 回复包含了本次被迭代的元素。</p>
<p><em>SCAN</em> 命令返回的每个元素都是一个数据库键。</p>
<p><em>SSCAN</em> 命令返回的每个元素都是一个集合成员。</p>
<p><em>HSCAN</em> 命令返回的每个元素都是一个键值对，一个键值对由一个键和一个值组成。</p>
<p><em>ZSCAN</em> 命令返回的每个元素都是一个有序集合元素，一个有序集合元素由一个成员（member）和一个分值（score）组成。</p>
</blockquote>
<h1 id="二、String（字符串）"><a href="#二、String（字符串）" class="headerlink" title="二、String（字符串）"></a>二、String（字符串）</h1><h2 id="1-APPEND"><a href="#1-APPEND" class="headerlink" title="1.APPEND"></a>1.APPEND</h2><p><strong>APPEND key value</strong></p>
<p>如果 <code>key</code> 已经存在并且是一个字符串， APPEND 命令将 <code>value</code> 追加到 <code>key</code> 原来的值的末尾。</p>
<p>如果 <code>key</code> 不存在， APPEND 就简单地将给定 <code>key</code> 设为 <code>value</code> ，就像执行 <code>SET key value</code> 一样。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>平摊O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>追加 <code>value</code> 之后， <code>key</code> 中字符串的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 对不存在的 key 执行 APPEND</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS myphone               # 确保 myphone 不存在</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; APPEND myphone &quot;nokia&quot;       # 对不存在的 key 进行 APPEND ，等同于 SET myphone &quot;nokia&quot;</span><br><span class="line">(integer) 5                         # 字符长度</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对已存在的字符串进行 APPEND</span><br><span class="line"></span><br><span class="line">redis&gt; APPEND myphone &quot; - 1110&quot;     # 长度从 5 个字符增加到 12 个字符</span><br><span class="line">(integer) 12</span><br><span class="line"></span><br><span class="line">redis&gt; GET myphone</span><br><span class="line">&quot;nokia - 1110&quot;</span><br></pre></td></tr></table></figure>

<h3 id="模式：时间序列-Time-series"><a href="#模式：时间序列-Time-series" class="headerlink" title="模式：时间序列(Time series)"></a>模式：时间序列(Time series)</h3><p>APPEND 可以为一系列定长(fixed-size)数据(sample)提供一种紧凑的表示方式，通常称之为时间序列。</p>
<p>每当一个新数据到达的时候，执行以下命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">APPEND timeseries &quot;fixed-size sample&quot;</span><br></pre></td></tr></table></figure>

<p>然后可以通过以下的方式访问时间序列的各项属性：</p>
<ul>
<li><em>STRLEN</em> 给出时间序列中数据的数量</li>
<li><em>GETRANGE</em> 可以用于随机访问。只要有相关的时间信息的话，我们就可以在 Redis 2.6 中使用 Lua 脚本和 <em>GETRANGE</em> 命令实现二分查找。</li>
<li><em>SETRANGE</em> 可以用于覆盖或修改已存在的的时间序列。</li>
</ul>
<p>这个模式的唯一缺陷是我们只能增长时间序列，而不能对时间序列进行缩短，因为 Redis 目前还没有对字符串进行修剪(tirm)的命令，但是，不管怎么说，这个模式的储存方式还是可以节省下大量的空间。</p>
<p>可以考虑使用 UNIX 时间戳作为时间序列的键名，这样一来，可以避免单个 <code>key</code> 因为保存过大的时间序列而占用大量内存，另一方面，也可以节省下大量命名空间。</p>
<p>下面是一个时间序列的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; APPEND ts &quot;0043&quot;</span><br><span class="line">(integer) 4</span><br><span class="line"></span><br><span class="line">redis&gt; APPEND ts &quot;0035&quot;</span><br><span class="line">(integer) 8</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE ts 0 3</span><br><span class="line">&quot;0043&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE ts 4 7</span><br><span class="line">&quot;0035&quot;</span><br></pre></td></tr></table></figure>

<h2 id="2-BITCOUNT"><a href="#2-BITCOUNT" class="headerlink" title="2.BITCOUNT"></a>2.BITCOUNT</h2><p><strong>BITCOUNT key [start] [end]</strong></p>
<p>计算给定字符串中，被设置为 <code>1</code> 的比特位的数量。</p>
<p>一般情况下，给定的整个字符串都会被进行计数，通过指定额外的 <code>start</code> 或 <code>end</code> 参数，可以让计数只在特定的位上进行。</p>
<p><code>start</code> 和 <code>end</code> 参数的设置和 <em>GETRANGE</em> 命令类似，都可以使用负数值：比如 <code>-1</code> 表示最后一个位，而 <code>-2</code> 表示倒数第二个位，以此类推。</p>
<p>不存在的 <code>key</code> 被当成是空字符串来处理，因此对一个不存在的 <code>key</code> 进行 <code>BITCOUNT</code> 操作，结果为 <code>0</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>被设置为 <code>1</code> 的位的数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; BITCOUNT bits</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits 0 1          # 0001</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; BITCOUNT bits</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits 3 1          # 1001</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; BITCOUNT bits</span><br><span class="line">(integer) 2</span><br></pre></td></tr></table></figure>

<h3 id="模式：使用-bitmap-实现用户上线次数统计"><a href="#模式：使用-bitmap-实现用户上线次数统计" class="headerlink" title="模式：使用 bitmap 实现用户上线次数统计"></a>模式：使用 bitmap 实现用户上线次数统计</h3><p>Bitmap 对于一些特定类型的计算非常有效。</p>
<p>假设现在我们希望记录自己网站上的用户的上线频率，比如说，计算用户 A 上线了多少天，用户 B 上线了多少天，诸如此类，以此作为数据，从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 [<em>SETBIT</em> 和 [<em>BITCOUNT</em> 来实现。</p>
<p>比如说，每当用户在某一天上线的时候，我们就使用[<em>SETBIT</em> ，以用户名作为 <code>key</code> ，将那天所代表的网站的上线日作为 <code>offset</code> 参数，并将这个 <code>offset</code> 上的为设置为 <code>1</code> 。</p>
<p>举个例子，如果今天是网站上线的第 100 天，而用户 peter 在今天阅览过网站，那么执行命令 <code>SETBIT peter 100 1</code> ；如果明天 peter 也继续阅览网站，那么执行命令 <code>SETBIT peter 101 1</code> ，以此类推。</p>
<p>当要计算 peter 总共以来的上线次数时，就使用 <em>BITCOUNT</em> 命令：执行 <code>BITCOUNT peter</code> ，得出的结果就是 peter 上线的总天数。</p>
<p>更详细的实现可以参考博文(墙外) <a href="http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/" target="_blank" rel="noopener">Fast, easy, realtime metrics using Redis bitmaps</a> 。</p>
<h3 id="性能"><a href="#性能" class="headerlink" title="性能"></a>性能</h3><p>前面的上线次数统计例子，即使运行 10 年，占用的空间也只是每个用户 10<em>365 比特位(bit)，也即是每个用户 456 字节。对于这种大小的数据来说， *BITCOUNT</em> 的处理速度就像 <em>GET</em> 和 <em>INCR</em> 这种 O(1) 复杂度的操作一样快。</p>
<p>如果你的 bitmap 数据非常大，那么可以考虑使用以下两种方法：</p>
<ol>
<li>将一个大的 bitmap 分散到不同的 key 中，作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。</li>
<li>使用 <em>BITCOUNT</em> 的 <code>start</code> 和 <code>end</code> 参数，每次只对所需的部分位进行计算，将位的累积工作(accumulating)放到客户端进行，并且对结果进行缓存 (caching)。</li>
</ol>
<h2 id="3-BITOP"><a href="#3-BITOP" class="headerlink" title="3.BITOP"></a>3.BITOP</h2><p><strong>BITOP operation destkey key [key …]</strong></p>
<p>对一个或多个保存二进制位的字符串 <code>key</code> 进行位元操作，并将结果保存到 <code>destkey</code> 上。</p>
<p><code>operation</code> 可以是 <code>AND</code> 、 <code>OR</code> 、 <code>NOT</code> 、 <code>XOR</code> 这四种操作中的任意一种：</p>
<ul>
<li><code>BITOP AND destkey key [key ...]</code> ，对一个或多个 <code>key</code> 求逻辑并，并将结果保存到 <code>destkey</code> 。</li>
<li><code>BITOP OR destkey key [key ...]</code> ，对一个或多个 <code>key</code> 求逻辑或，并将结果保存到 <code>destkey</code> 。</li>
<li><code>BITOP XOR destkey key [key ...]</code> ，对一个或多个 <code>key</code> 求逻辑异或，并将结果保存到 <code>destkey</code> 。</li>
<li><code>BITOP NOT destkey key</code> ，对给定 <code>key</code> 求逻辑非，并将结果保存到 <code>destkey</code> 。</li>
</ul>
<p>除了 <code>NOT</code> 操作之外，其他操作都可以接受一个或多个 <code>key</code> 作为输入。</p>
<p><strong>处理不同长度的字符串</strong></p>
<p>当 BITOP 处理不同长度的字符串时，较短的那个字符串所缺少的部分会被看作 <code>0</code> 。</p>
<p>空的 <code>key</code> 也被看作是包含 <code>0</code> 的字符串序列。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>保存到 <code>destkey</code> 的字符串的长度，和输入 <code>key</code> 中最长的字符串长度相等。</p>
</li>
</ul>
<p>BITOP 的复杂度为 O(N) ，当处理大型矩阵(matrix)或者进行大数据量的统计时，最好将任务指派到附属节点(slave)进行，避免阻塞主节点。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SETBIT bits-1 0 1        # bits-1 &#x3D; 1001</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits-1 3 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits-2 0 1        # bits-2 &#x3D; 1011</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits-2 1 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bits-2 3 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; BITOP AND and-result bits-1 bits-2</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT and-result 0      # and-result &#x3D; 1001</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT and-result 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT and-result 2</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT and-result 3</span><br><span class="line">(integer) 1</span><br></pre></td></tr></table></figure>

<h2 id="4-DECR"><a href="#4-DECR" class="headerlink" title="4.DECR"></a>4.DECR</h2><p><strong>DECR key</strong></p>
<p>将 <code>key</code> 中储存的数字值减一。</p>
<p>如果 <code>key</code> 不存在，那么 <code>key</code> 的值会先被初始化为 <code>0</code> ，然后再执行 DECR 操作。</p>
<p>如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。</p>
<p>本操作的值限制在 64 位(bit)有符号数字表示之内。</p>
<p>关于递增(increment) / 递减(decrement)操作的更多信息，请参见 [<em>INCR</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行 DECR 命令之后 <code>key</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 对存在的数字值 key 进行 DECR</span><br><span class="line"></span><br><span class="line">redis&gt; SET failure_times 10</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DECR failure_times</span><br><span class="line">(integer) 9</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对不存在的 key 值进行 DECR</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS count</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; DECR count</span><br><span class="line">(integer) -1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对存在但不是数值的 key 进行 DECR</span><br><span class="line"></span><br><span class="line">redis&gt; SET company YOUR_CODE_SUCKS.LLC</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DECR company</span><br><span class="line">(error) ERR value is not an integer or out of range</span><br></pre></td></tr></table></figure>

<h2 id="5-DECRBY"><a href="#5-DECRBY" class="headerlink" title="5.DECRBY"></a>5.DECRBY</h2><p><strong>DECRBY key decrement</strong></p>
<p>将 <code>key</code> 所储存的值减去减量 <code>decrement</code> 。</p>
<p>如果 <code>key</code> 不存在，那么 <code>key</code> 的值会先被初始化为 <code>0</code> ，然后再执行 DECRBY 操作。</p>
<p>如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。</p>
<p>本操作的值限制在 64 位(bit)有符号数字表示之内。</p>
<p>关于更多递增(increment) / 递减(decrement)操作的更多信息，请参见 [<em>INCR</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>减去 <code>decrement</code> 之后， <code>key</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 对已存在的 key 进行 DECRBY</span><br><span class="line"></span><br><span class="line">redis&gt; SET count 100</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DECRBY count 20</span><br><span class="line">(integer) 80</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对不存在的 key 进行DECRBY</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS pages</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; DECRBY pages 10</span><br><span class="line">(integer) -10</span><br></pre></td></tr></table></figure>

<h2 id="6-GET"><a href="#6-GET" class="headerlink" title="6.GET"></a>6.GET</h2><p><strong>GET key</strong></p>
<p>返回 <code>key</code> 所关联的字符串值。</p>
<p>如果 <code>key</code> 不存在那么返回特殊值 <code>nil</code> 。</p>
<p>假如 <code>key</code> 储存的值不是字符串类型，返回一个错误，因为 [GET 只能用于处理字符串值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当 <code>key</code> 不存在时，返回 <code>nil</code> ，否则，返回 <code>key</code> 的值。如果 <code>key</code> 不是字符串类型，那么返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"># 对不存在的 key 或字符串类型 key 进行 GET</span><br><span class="line"></span><br><span class="line">redis&gt; GET db</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line">redis&gt; SET db redis</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET db</span><br><span class="line">&quot;redis&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对不是字符串类型的 key 进行 GET</span><br><span class="line"></span><br><span class="line">redis&gt; DEL db</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH db redis mongodb mysql</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; GET db</span><br><span class="line">(error) ERR Operation against a key holding the wrong kind of value</span><br></pre></td></tr></table></figure>

<h2 id="7-GETBIT"><a href="#7-GETBIT" class="headerlink" title="7.GETBIT"></a>7.GETBIT</h2><p><strong>GETBIT key offset</strong></p>
<p>对 <code>key</code> 所储存的字符串值，获取指定偏移量上的位(bit)。</p>
<p>当 <code>offset</code> 比字符串值的长度大，或者 <code>key</code> 不存在时，返回 <code>0</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>字符串值指定偏移量上的位(bit)。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 对不存在的 key 或者不存在的 offset 进行 GETBIT， 返回 0</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS bit</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT bit 10086</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对已存在的 offset 进行 GETBIT</span><br><span class="line"></span><br><span class="line">redis&gt; SETBIT bit 10086 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT bit 10086</span><br><span class="line">(integer) 1</span><br></pre></td></tr></table></figure>

<h2 id="8-GETRANGE"><a href="#8-GETRANGE" class="headerlink" title="8.GETRANGE"></a>8.GETRANGE</h2><p><strong>GETRANGE key start end</strong></p>
<p>返回 <code>key</code> 中字符串值的子字符串，字符串的截取范围由 <code>start</code> 和 <code>end</code> 两个偏移量决定(包括 <code>start</code> 和 <code>end</code> 在内)。</p>
<p>负数偏移量表示从字符串最后开始计数， <code>-1</code> 表示最后一个字符， <code>-2</code> 表示倒数第二个，以此类推。</p>
<p>GETRANGE 通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。</p>
<p>在 &lt;= 2.0 的版本里，GETRANGE 被叫作 SUBSTR。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.4.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要返回的字符串的长度。复杂度最终由字符串的返回值长度决定，但因为从已有字符串中取出子字符串的操作非常廉价(cheap)，所以对于长度不大的字符串，该操作的复杂度也可看作O(1)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>截取得出的子字符串。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET greeting &quot;hello, my friend&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE greeting 0 4          # 返回索引0-4的字符，包括4。</span><br><span class="line">&quot;hello&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE greeting -1 -5        # 不支持回绕操作</span><br><span class="line">&quot;&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE greeting -3 -1        # 负数索引</span><br><span class="line">&quot;end&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE greeting 0 -1         # 从第一个到最后一个</span><br><span class="line">&quot;hello, my friend&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETRANGE greeting 0 1008611    # 值域范围不超过实际字符串，超过部分自动被符略</span><br><span class="line">&quot;hello, my friend&quot;</span><br></pre></td></tr></table></figure>

<h2 id="9-GETSET"><a href="#9-GETSET" class="headerlink" title="9.GETSET"></a>9.GETSET</h2><p><strong>GETSET key value</strong></p>
<p>将给定 <code>key</code> 的值设为 <code>value</code> ，并返回 <code>key</code> 的旧值(old value)。</p>
<p>当 <code>key</code> 存在但不是字符串类型时，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>返回给定 <code>key</code> 的旧值。当 <code>key</code> 没有旧值时，也即是， <code>key</code> 不存在时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; GETSET db mongodb    # 没有旧值，返回 nil</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line">redis&gt; GET db</span><br><span class="line">&quot;mongodb&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GETSET db redis      # 返回旧值 mongodb</span><br><span class="line">&quot;mongodb&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GET db</span><br><span class="line">&quot;redis&quot;</span><br></pre></td></tr></table></figure>

<h3 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h3><p>GETSET 可以和 <em>INCR</em> 组合使用，实现一个有原子性(atomic)复位操作的计数器(counter)。</p>
<p>举例来说，每次当某个事件发生时，进程可能对一个名为 <code>mycount</code> 的 <code>key</code> 调用 <em>INCR</em> 操作，通常我们还要在一个原子时间内同时完成获得计数器的值和将计数器值复位为 <code>0</code> 两个操作。</p>
<p>可以用命令 <code>GETSET mycounter 0</code> 来实现这一目标。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; INCR mycount</span><br><span class="line">(integer) 11</span><br><span class="line"></span><br><span class="line">redis&gt; GETSET mycount 0  # 一个原子内完成 GET mycount 和 SET mycount 0 操作</span><br><span class="line">&quot;11&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GET mycount       # 计数器被重置</span><br><span class="line">&quot;0&quot;</span><br></pre></td></tr></table></figure>

<h2 id="10-INCR"><a href="#10-INCR" class="headerlink" title="10.INCR"></a>10.INCR</h2><p><strong>INCR key</strong></p>
<p>将 <code>key</code> 中储存的数字值增一。</p>
<p>如果 <code>key</code> 不存在，那么 <code>key</code> 的值会先被初始化为 <code>0</code> ，然后再执行 [INCR 操作。</p>
<p>如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。</p>
<p>本操作的值限制在 64 位(bit)有符号数字表示之内。</p>
<p>这是一个针对字符串的操作，因为 Redis 没有专用的整数类型，所以 key 内储存的字符串被解释为十进制 64 位有符号整数来执行 INCR 操作。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行 INCR 命令之后 <code>key</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET page_view 20</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCR page_view</span><br><span class="line">(integer) 21</span><br><span class="line"></span><br><span class="line">redis&gt; GET page_view    # 数字值在 Redis 中以字符串的形式保存</span><br><span class="line">&quot;21&quot;</span><br></pre></td></tr></table></figure>

<h3 id="模式：计数器"><a href="#模式：计数器" class="headerlink" title="模式：计数器"></a>模式：计数器</h3><p>计数器是 Redis 的原子性自增操作可实现的最直观的模式了，它的想法相当简单：每当某个操作发生时，向 Redis 发送一个 INCR 命令。</p>
<p>比如在一个 web 应用程序中，如果想知道用户在一年中每天的点击量，那么只要将用户 ID 以及相关的日期信息作为键，并在每次用户点击页面时，执行一次自增操作即可。</p>
<p>比如用户名是 <code>peter</code> ，点击时间是 2012 年 3 月 22 日，那么执行命令 <code>INCR peter::2012.3.22</code> 。</p>
<p>可以用以下几种方式扩展这个简单的模式：</p>
<ul>
<li>可以通过组合使用 INCR 和 <em>EXPIRE</em> ，来达到只在规定的生存时间内进行计数(counting)的目的。</li>
<li>客户端可以通过使用 <em>GETSET</em> 命令原子性地获取计数器的当前值并将计数器清零，更多信息请参考 <em>GETSET</em> 命令。</li>
<li>使用其他自增/自减操作，比如 <em>DECR</em> 和 <em>INCRBY</em> ，用户可以通过执行不同的操作增加或减少计数器的值，比如在游戏中的记分器就可能用到这些命令。</li>
</ul>
<h3 id="模式：限速器"><a href="#模式：限速器" class="headerlink" title="模式：限速器"></a>模式：限速器</h3><p>限速器是特殊化的计算器，它用于限制一个操作可以被执行的速率(rate)。</p>
<p>限速器的典型用法是限制公开 API 的请求次数，以下是一个限速器实现示例，它将 API 的最大请求数限制在每个 IP 地址每秒钟十个之内：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">FUNCTION LIMIT_API_CALL(ip)</span><br><span class="line">ts &#x3D; CURRENT_UNIX_TIME()</span><br><span class="line">keyname &#x3D; ip+&quot;:&quot;+ts</span><br><span class="line">current &#x3D; GET(keyname)</span><br><span class="line"></span><br><span class="line">IF current !&#x3D; NULL AND current &gt; 10 THEN</span><br><span class="line">    ERROR &quot;too many requests per second&quot;</span><br><span class="line">END</span><br><span class="line"></span><br><span class="line">IF current &#x3D;&#x3D; NULL THEN</span><br><span class="line">    MULTI</span><br><span class="line">        INCR(keyname, 1)</span><br><span class="line">        EXPIRE(keyname, 1)</span><br><span class="line">    EXEC</span><br><span class="line">ELSE</span><br><span class="line">    INCR(keyname, 1)</span><br><span class="line">END</span><br><span class="line"></span><br><span class="line">PERFORM_API_CALL()</span><br></pre></td></tr></table></figure>

<p>这个实现每秒钟为每个 IP 地址使用一个不同的计数器，并用 [<em>EXPIRE</em> 命令设置生存时间(这样 Redis 就会负责自动删除过期的计数器)。</p>
<p>注意，我们使用事务打包执行 <em>INCR</em> 命令和 <em>EXPIRE</em> 命令，避免引入竞争条件，保证每次调用 API 时都可以正确地对计数器进行自增操作并设置生存时间。</p>
<p>以下是另一个限速器实现：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">FUNCTION LIMIT_API_CALL(ip):</span><br><span class="line">current &#x3D; GET(ip)</span><br><span class="line">IF current !&#x3D; NULL AND current &gt; 10 THEN</span><br><span class="line">    ERROR &quot;too many requests per second&quot;</span><br><span class="line">ELSE</span><br><span class="line">    value &#x3D; INCR(ip)</span><br><span class="line">    IF value &#x3D;&#x3D; 1 THEN</span><br><span class="line">        EXPIRE(ip,1)</span><br><span class="line">    END</span><br><span class="line">    PERFORM_API_CALL()</span><br><span class="line">END</span><br></pre></td></tr></table></figure>

<p>这个限速器只使用单个计数器，它的生存时间为一秒钟，如果在一秒钟内，这个计数器的值大于 <code>10</code> 的话，那么访问就会被禁止。</p>
<p>这个新的限速器在思路方面是没有问题的，但它在实现方面不够严谨，如果我们仔细观察一下的话，就会发现在 <em>INCR</em> 和 <em>EXPIRE</em> 之间存在着一个竞争条件，假如客户端在执行 <em>INCR</em> 之后，因为某些原因(比如客户端失败)而忘记设置 <em>EXPIRE</em> 的话，那么这个计数器就会一直存在下去，造成每个用户只能访问 <code>10</code> 次，噢，这简直是个灾难！</p>
<p>要消灭这个实现中的竞争条件，我们可以将它转化为一个 Lua 脚本，并放到 Redis 中运行(这个方法仅限于 Redis 2.6 及以上的版本)：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">local current</span><br><span class="line">current &#x3D; redis.call(&quot;incr&quot;,KEYS[1])</span><br><span class="line">if tonumber(current) &#x3D;&#x3D; 1 then</span><br><span class="line">    redis.call(&quot;expire&quot;,KEYS[1],1)</span><br><span class="line">end</span><br></pre></td></tr></table></figure>

<p>通过将计数器作为脚本放到 Redis 上运行，我们保证了 <em>INCR</em> 和 <em>EXPIRE</em> 两个操作的原子性，现在这个脚本实现不会引入竞争条件，它可以运作的很好。</p>
<p>关于在 Redis 中运行 Lua 脚本的更多信息，请参考 <em>EVAL</em> 命令。</p>
<p>还有另一种消灭竞争条件的方法，就是使用 Redis 的列表结构来代替 <em>INCR</em> 命令，这个方法无须脚本支持，因此它在 Redis 2.6 以下的版本也可以运行得很好：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">FUNCTION LIMIT_API_CALL(ip)</span><br><span class="line">current &#x3D; LLEN(ip)</span><br><span class="line">IF current &gt; 10 THEN</span><br><span class="line">    ERROR &quot;too many requests per second&quot;</span><br><span class="line">ELSE</span><br><span class="line">    IF EXISTS(ip) &#x3D;&#x3D; FALSE</span><br><span class="line">        MULTI</span><br><span class="line">            RPUSH(ip,ip)</span><br><span class="line">            EXPIRE(ip,1)</span><br><span class="line">        EXEC</span><br><span class="line">    ELSE</span><br><span class="line">        RPUSHX(ip,ip)</span><br><span class="line">    END</span><br><span class="line">    PERFORM_API_CALL()</span><br><span class="line">END</span><br></pre></td></tr></table></figure>

<p>新的限速器使用了列表结构作为容器， <em>LLEN</em> 用于对访问次数进行检查，一个事务包裹着 <em>RPUSH</em> 和 <em>EXPIRE</em> 两个命令，用于在第一次执行计数时创建列表，并正确设置地设置过期时间，最后， <em>RPUSHX</em> 在后续的计数操作中进行增加操作。</p>
<h2 id="11-INCRBY"><a href="#11-INCRBY" class="headerlink" title="11.INCRBY"></a>11.INCRBY</h2><p><strong>INCRBY key increment</strong></p>
<p>将 <code>key</code> 所储存的值加上增量 <code>increment</code> 。</p>
<p>如果 <code>key</code> 不存在，那么 <code>key</code> 的值会先被初始化为 <code>0</code> ，然后再执行 INCRBY 命令。</p>
<p>如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。</p>
<p>本操作的值限制在 64 位(bit)有符号数字表示之内。</p>
<p>关于递增(increment) / 递减(decrement)操作的更多信息，参见 [<em>INCR</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>加上 <code>increment</code> 之后， <code>key</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"># key 存在且是数字值</span><br><span class="line"></span><br><span class="line">redis&gt; SET rank 50</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBY rank 20</span><br><span class="line">(integer) 70</span><br><span class="line"></span><br><span class="line">redis&gt; GET rank</span><br><span class="line">&quot;70&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 不存在时</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS counter</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBY counter 30</span><br><span class="line">(integer) 30</span><br><span class="line"></span><br><span class="line">redis&gt; GET counter</span><br><span class="line">&quot;30&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 不是数字值时</span><br><span class="line"></span><br><span class="line">redis&gt; SET book &quot;long long ago...&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBY book 200</span><br><span class="line">(error) ERR value is not an integer or out of range</span><br></pre></td></tr></table></figure>

<h2 id="12-INCRBYFLOAT"><a href="#12-INCRBYFLOAT" class="headerlink" title="12.INCRBYFLOAT"></a>12.INCRBYFLOAT</h2><p><strong>INCRBYFLOAT key increment</strong></p>
<p>为 <code>key</code> 中所储存的值加上浮点数增量 <code>increment</code> 。</p>
<p>如果 <code>key</code> 不存在，那么 INCRBYFLOAT 会先将 <code>key</code> 的值设为 <code>0</code> ，再执行加法操作。</p>
<p>如果命令执行成功，那么 <code>key</code> 的值会被更新为（执行加法之后的）新值，并且新值会以字符串的形式返回给调用者。</p>
<p>无论是 <code>key</code> 的值，还是增量 <code>increment</code> ，都可以使用像 <code>2.0e7</code> 、 <code>3e5</code> 、 <code>90e-2</code> 那样的指数符号(exponential notation)来表示，但是，<strong>执行 INCRBYFLOAT 命令之后的值</strong>总是以同样的形式储存，也即是，它们总是由一个数字，一个（可选的）小数点和一个任意位的小数部分组成（比如 <code>3.14</code> 、 <code>69.768</code> ，诸如此类)，小数部分尾随的 <code>0</code> 会被移除，如果有需要的话，还会将浮点数改为整数（比如 <code>3.0</code> 会被保存成 <code>3</code> ）。</p>
<p>除此之外，无论加法计算所得的浮点数的实际精度有多长， INCRBYFLOAT 的计算结果也最多只能表示小数点的后十七位。</p>
<p>当以下任意一个条件发生时，返回一个错误：</p>
<ul>
<li><p><code>key</code> 的值不是字符串类型(因为 Redis 中的数字和浮点数都以字符串的形式保存，所以它们都属于字符串类型）</p>
</li>
<li><p><code>key</code> 当前的值或者给定的增量 <code>increment</code> 不能解释(parse)为双精度浮点数(double precision floating point number）</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行命令之后 <code>key</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"># 值和增量都不是指数符号</span><br><span class="line"></span><br><span class="line">redis&gt; SET mykey 10.50</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBYFLOAT mykey 0.1</span><br><span class="line">&quot;10.6&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 值和增量都是指数符号</span><br><span class="line"></span><br><span class="line">redis&gt; SET mykey 314e-2</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET mykey                # 用 SET 设置的值可以是指数符号</span><br><span class="line">&quot;314e-2&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBYFLOAT mykey 0      # 但执行 INCRBYFLOAT 之后格式会被改成非指数符号</span><br><span class="line">&quot;3.14&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 可以对整数类型执行</span><br><span class="line"></span><br><span class="line">redis&gt; SET mykey 3</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBYFLOAT mykey 1.1</span><br><span class="line">&quot;4.1&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 后跟的 0 会被移除</span><br><span class="line"></span><br><span class="line">redis&gt; SET mykey 3.0</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET mykey                                    # SET 设置的值小数部分可以是 0</span><br><span class="line">&quot;3.0&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; INCRBYFLOAT mykey 1.000000000000000000000    # 但 INCRBYFLOAT 会将无用的 0 忽略掉，有需要的话，将浮点变为整数</span><br><span class="line">&quot;4&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; GET mykey</span><br><span class="line">&quot;4&quot;</span><br></pre></td></tr></table></figure>

<h2 id="13-MGET"><a href="#13-MGET" class="headerlink" title="13.MGET"></a>13.MGET</h2><p><strong>MGET key [key …]</strong></p>
<p>返回所有(一个或多个)给定 <code>key</code> 的值。</p>
<p>如果给定的 <code>key</code> 里面，有某个 <code>key</code> 不存在，那么这个 <code>key</code> 返回特殊值 <code>nil</code> 。因此，该命令永不失败。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N) , <code>N</code> 为给定 <code>key</code> 的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个包含所有给定 <code>key</code> 的值的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET redis redis.com</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET mongodb mongodb.org</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MGET redis mongodb</span><br><span class="line">1) &quot;redis.com&quot;</span><br><span class="line">2) &quot;mongodb.org&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; MGET redis mongodb mysql     # 不存在的 mysql 返回 nil</span><br><span class="line">1) &quot;redis.com&quot;</span><br><span class="line">2) &quot;mongodb.org&quot;</span><br><span class="line">3) (nil)</span><br></pre></td></tr></table></figure>

<h2 id="14-MSET"><a href="#14-MSET" class="headerlink" title="14.MSET"></a>14.MSET</h2><p><strong>MSET key value [key value …]</strong></p>
<p>同时设置一个或多个 <code>key-value</code> 对。</p>
<p>如果某个给定 <code>key</code> 已经存在，那么 MSET 会用新值覆盖原来的旧值，如果这不是你所希望的效果，请考虑使用 <em>MSETNX</em> 命令：它只会在所有给定 <code>key</code> 都不存在的情况下进行设置操作。</p>
<p>MSET 是一个原子性(atomic)操作，所有给定 <code>key</code> 都会在同一时间内被设置，某些给定 <code>key</code> 被更新而另一些给定 <code>key</code> 没有改变的情况，不可能发生。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.1</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要设置的 <code>key</code> 数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> (因为 <code>MSET</code> 不可能失败)</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; MSET date &quot;2012.3.30&quot; time &quot;11:00 a.m.&quot; weather &quot;sunny&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MGET date time weather</span><br><span class="line">1) &quot;2012.3.30&quot;</span><br><span class="line">2) &quot;11:00 a.m.&quot;</span><br><span class="line">3) &quot;sunny&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># MSET 覆盖旧值例子</span><br><span class="line"></span><br><span class="line">redis&gt; SET google &quot;google.hk&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MSET google &quot;google.com&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET google</span><br><span class="line">&quot;google.com&quot;</span><br></pre></td></tr></table></figure>

<h2 id="15-MSETNX"><a href="#15-MSETNX" class="headerlink" title="15.MSETNX"></a>15.MSETNX</h2><p><strong>MSETNX key value [key value …]</strong></p>
<p>同时设置一个或多个 <code>key-value</code> 对，当且仅当所有给定 <code>key</code> 都不存在。</p>
<p>即使只有一个给定 <code>key</code> 已存在， [MSETNX 也会拒绝执行所有给定 <code>key</code> 的设置操作。</p>
<p>MSETNX 是原子性的，因此它可以用作设置多个不同 <code>key</code> 表示不同字段(field)的唯一性逻辑对象(unique logic object)，所有字段要么全被设置，要么全不被设置。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.1</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要设置的 <code>key</code> 的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当所有 <code>key</code> 都成功设置，返回 <code>1</code> 。如果所有给定 <code>key</code> 都设置失败(至少有一个 <code>key</code> 已经存在)，那么返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"># 对不存在的 key 进行 MSETNX</span><br><span class="line"></span><br><span class="line">redis&gt; MSETNX rmdbs &quot;MySQL&quot; nosql &quot;MongoDB&quot; key-value-store &quot;redis&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; MGET rmdbs nosql key-value-store</span><br><span class="line">1) &quot;MySQL&quot;</span><br><span class="line">2) &quot;MongoDB&quot;</span><br><span class="line">3) &quot;redis&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># MSET 的给定 key 当中有已存在的 key</span><br><span class="line"></span><br><span class="line">redis&gt; MSETNX rmdbs &quot;Sqlite&quot; language &quot;python&quot;  # rmdbs 键已经存在，操作失败</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS language                          # 因为 MSET 是原子性操作，language 没有被设置</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GET rmdbs                                # rmdbs 也没有被修改</span><br><span class="line">&quot;MySQL&quot;</span><br></pre></td></tr></table></figure>

<h2 id="16-PSETEX"><a href="#16-PSETEX" class="headerlink" title="16.PSETEX"></a>16.PSETEX</h2><p><strong>PSETEX key milliseconds value</strong></p>
<p>这个命令和 <em>SETEX</em> 命令相似，但它以毫秒为单位设置 <code>key</code> 的生存时间，而不是像 <em>SETEX</em> 命令那样，以秒为单位。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功时返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; PSETEX mykey 1000 &quot;Hello&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PTTL mykey</span><br><span class="line">(integer) 999</span><br><span class="line"></span><br><span class="line">redis&gt; GET mykey</span><br><span class="line">&quot;Hello&quot;</span><br></pre></td></tr></table></figure>

<h2 id="17-SET"><a href="#17-SET" class="headerlink" title="17.SET"></a>17.SET</h2><p><strong>SET key value [EX seconds] [PX milliseconds] [NX|XX]</strong></p>
<p>将字符串值 <code>value</code> 关联到 <code>key</code> 。</p>
<p>如果 <code>key</code> 已经持有其他值， SET 就覆写旧值，无视类型。</p>
<p>对于某个原本带有生存时间（TTL）的键来说， 当 <em>SET</em> 命令成功在这个键上执行时， 这个键原有的 TTL 将被清除。</p>
<p><strong>可选参数</strong></p>
<p>从 Redis 2.6.12 版本开始， <em>SET</em> 命令的行为可以通过一系列参数来修改：</p>
<ul>
<li><code>EX second</code> ：设置键的过期时间为 <code>second</code> 秒。 <code>SET key value EX second</code> 效果等同于 <code>SETEX key second value</code> 。</li>
<li><code>PX millisecond</code> ：设置键的过期时间为 <code>millisecond</code> 毫秒。 <code>SET key value PX millisecond</code> 效果等同于 <code>PSETEX key millisecond value</code> 。</li>
<li><code>NX</code> ：只在键不存在时，才对键进行设置操作。 <code>SET key value NX</code> 效果等同于 <code>SETNX key value</code> 。</li>
<li><code>XX</code> ：只在键已经存在时，才对键进行设置操作。</li>
</ul>
<p>因为 <em>SET</em> 命令可以通过参数来实现和[<em>SETNX</em> 、 <em>SETEX</em> 和 <em>PSETEX</em> 三个命令的效果，所以将来的 Redis 版本可能会废弃并最终移除 <em>SETNX</em> 、 <em>SETEX</em> 和 <em>PSETEX</em> 这三个命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>在 Redis 2.6.12 版本以前， <em>SET</em> 命令总是返回 <code>OK</code> 。从 Redis 2.6.12 版本开始， <em>SET</em> 在设置操作成功完成时，才返回 <code>OK</code> 。如果设置了 <code>NX</code> 或者 <code>XX</code> ，但因为条件没达到而造成设置操作未执行，那么命令返回空批量回复（NULL Bulk Reply）。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"># 对不存在的键进行设置</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key &quot;value&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key</span><br><span class="line">&quot;value&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对已存在的键进行设置</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key &quot;new-value&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key</span><br><span class="line">&quot;new-value&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 使用 EX 选项</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key-with-expire-time &quot;hello&quot; EX 10086</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key-with-expire-time</span><br><span class="line">&quot;hello&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; TTL key-with-expire-time</span><br><span class="line">(integer) 10069</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 使用 PX 选项</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key-with-pexpire-time &quot;moto&quot; PX 123321</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key-with-pexpire-time</span><br><span class="line">&quot;moto&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; PTTL key-with-pexpire-time</span><br><span class="line">(integer) 111939</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 使用 NX 选项</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET not-exists-key &quot;value&quot; NX</span><br><span class="line">OK      # 键不存在，设置成功</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET not-exists-key</span><br><span class="line">&quot;value&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET not-exists-key &quot;new-value&quot; NX</span><br><span class="line">(nil)   # 键已经存在，设置失败</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GEt not-exists-key</span><br><span class="line">&quot;value&quot; # 维持原值不变</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 使用 XX 选项</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; EXISTS exists-key</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET exists-key &quot;value&quot; XX</span><br><span class="line">(nil)   # 因为键不存在，设置失败</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET exists-key &quot;value&quot;</span><br><span class="line">OK      # 先给键设置一个值</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET exists-key &quot;new-value&quot; XX</span><br><span class="line">OK      # 设置新值成功</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET exists-key</span><br><span class="line">&quot;new-value&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># NX 或 XX 可以和 EX 或者 PX 组合使用</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key-with-expire-and-NX &quot;hello&quot; EX 10086 NX</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key-with-expire-and-NX</span><br><span class="line">&quot;hello&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; TTL key-with-expire-and-NX</span><br><span class="line">(integer) 10063</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key-with-pexpire-and-XX &quot;old value&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key-with-pexpire-and-XX &quot;new value&quot; PX 123321</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; GET key-with-pexpire-and-XX</span><br><span class="line">&quot;new value&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; PTTL key-with-pexpire-and-XX</span><br><span class="line">(integer) 112999</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># EX 和 PX 可以同时出现，但后面给出的选项会覆盖前面给出的选项</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET key &quot;value&quot; EX 1000 PX 5000000</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; TTL key</span><br><span class="line">(integer) 4993  # 这是 PX 参数设置的值</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; SET another-key &quot;value&quot; PX 5000000 EX 1000</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; TTL another-key</span><br><span class="line">(integer) 997   # 这是 EX 参数设置的值</span><br></pre></td></tr></table></figure>

<h3 id="使用模式"><a href="#使用模式" class="headerlink" title="使用模式"></a>使用模式</h3><p>命令 <code>SET resource-name anystring NX EX max-lock-time</code> 是一种在 Redis 中实现锁的简单方法。</p>
<p>客户端执行以上的命令：</p>
<ul>
<li>如果服务器返回 <code>OK</code> ，那么这个客户端获得锁。</li>
<li>如果服务器返回 <code>NIL</code> ，那么客户端获取锁失败，可以在稍后再重试。</li>
</ul>
<p>设置的过期时间到达之后，锁将自动释放。</p>
<p>可以通过以下修改，让这个锁实现更健壮：</p>
<ul>
<li>不使用固定的字符串作为键的值，而是设置一个不可猜测（non-guessable）的长随机字符串，作为口令串（token）。</li>
<li>不使用 <em>DEL</em> 命令来释放锁，而是发送一个 Lua 脚本，这个脚本只在客户端传入的值和键的口令串相匹配时，才对键进行删除。</li>
</ul>
<p>这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。</p>
<p>以下是一个简单的解锁脚本示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if redis.call(&quot;get&quot;,KEYS[1]) &#x3D;&#x3D; ARGV[1]</span><br><span class="line">then</span><br><span class="line">    return redis.call(&quot;del&quot;,KEYS[1])</span><br><span class="line">else</span><br><span class="line">    return 0</span><br><span class="line">end</span><br></pre></td></tr></table></figure>

<p>这个脚本可以通过 <code>EVAL ...script... 1 resource-name token-value</code> 命令来调用。</p>
<h2 id="18-SETBIT"><a href="#18-SETBIT" class="headerlink" title="18.SETBIT"></a>18.SETBIT</h2><p><strong>SETBIT key offset value</strong></p>
<p>对 <code>key</code> 所储存的字符串值，设置或清除指定偏移量上的位(bit)。</p>
<p>位的设置或清除取决于 <code>value</code> 参数，可以是 <code>0</code> 也可以是 <code>1</code> 。</p>
<p>当 <code>key</code> 不存在时，自动生成一个新的字符串值。</p>
<p>字符串会进行伸展(grown)以确保它可以将 <code>value</code> 保存在指定的偏移量上。当字符串值进行伸展时，空白位置以 <code>0</code> 填充。</p>
<p><code>offset</code> 参数必须大于或等于 <code>0</code> ，小于 2^32 (bit 映射被限制在 512 MB 之内)。</p>
<p>对使用大的 <code>offset</code> 的 SETBIT 操作来说，内存分配可能造成 Redis 服务器被阻塞。具体参考 <em>SETRANGE</em> 命令，warning(警告)部分。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>指定偏移量原来储存的位。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SETBIT bit 10086 1</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT bit 10086</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; GETBIT bit 100   # bit 默认被初始化为 0</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="19-SETEX"><a href="#19-SETEX" class="headerlink" title="19.SETEX"></a>19.SETEX</h2><p><strong>SETEX key seconds value</strong></p>
<p>将值 <code>value</code> 关联到 <code>key</code> ，并将 <code>key</code> 的生存时间设为 <code>seconds</code> (以秒为单位)。</p>
<p>如果 <code>key</code> 已经存在， SETEX 命令将覆写旧值。</p>
<p>这个命令类似于以下两个命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SET key value</span><br><span class="line">EXPIRE key seconds  # 设置生存时间</span><br></pre></td></tr></table></figure>

<p>不同之处是， SETEX 是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成，该命令在 Redis 用作缓存时，非常实用。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功时返回 <code>OK</code> 。当 <code>seconds</code> 参数不合法时，返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 在 key 不存在时进行 SETEX</span><br><span class="line"></span><br><span class="line">redis&gt; SETEX cache_user_id 60 10086</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET cache_user_id  # 值</span><br><span class="line">&quot;10086&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; TTL cache_user_id  # 剩余生存时间</span><br><span class="line">(integer) 49</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 已经存在时，SETEX 覆盖旧值</span><br><span class="line"></span><br><span class="line">redis&gt; SET cd &quot;timeless&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SETEX cd 3000 &quot;goodbye my love&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; GET cd</span><br><span class="line">&quot;goodbye my love&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; TTL cd</span><br><span class="line">(integer) 2997</span><br></pre></td></tr></table></figure>

<h2 id="20-SETNX"><a href="#20-SETNX" class="headerlink" title="20.SETNX"></a>20.SETNX</h2><p><strong>SETNX key value</strong></p>
<p>将 <code>key</code> 的值设为 <code>value</code> ，当且仅当 <code>key</code> 不存在。</p>
<p>若给定的 <code>key</code> 已经存在，则 SETNX 不做任何动作。</p>
<p>SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功，返回 <code>1</code> 。设置失败，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; EXISTS job                # job 不存在</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETNX job &quot;programmer&quot;    # job 设置成功</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SETNX job &quot;code-farmer&quot;   # 尝试覆盖 job ，失败</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; GET job                   # 没有被覆盖</span><br><span class="line">&quot;programmer&quot;</span><br></pre></td></tr></table></figure>

<h2 id="21-SETRANGE"><a href="#21-SETRANGE" class="headerlink" title="21.SETRANGE"></a>21.SETRANGE</h2><p><strong>SETRANGE key offset value</strong></p>
<p>用 <code>value</code> 参数覆写(overwrite)给定 <code>key</code> 所储存的字符串值，从偏移量 <code>offset</code> 开始。</p>
<p>不存在的 <code>key</code> 当作空白字符串处理。</p>
<p>SETRANGE 命令会确保字符串足够长以便将 <code>value</code> 设置在指定的偏移量上，如果给定 <code>key</code> 原来储存的字符串长度比偏移量小(比如字符串只有 <code>5</code> 个字符长，但你设置的 <code>offset</code> 是 <code>10</code> )，那么原字符和偏移量之间的空白将用零字节(zerobytes, <code>&quot;\x00&quot;</code> )来填充。</p>
<p>注意你能使用的最大偏移量是 2^29-1(536870911) ，因为 Redis 字符串的大小被限制在 512 兆(megabytes)以内。如果你需要使用比这更大的空间，你可以使用多个 <code>key</code> 。</p>
<p>当生成一个很长的字符串时，Redis 需要分配内存空间，该操作有时候可能会造成服务器阻塞(block)。在2010年的Macbook Pro上，设置偏移量为 536870911(512MB 内存分配)，耗费约 300 毫秒， 设置偏移量为 134217728(128MB 内存分配)，耗费约 80 毫秒，设置偏移量 33554432(32MB 内存分配)，耗费约 30 毫秒，设置偏移量为 8388608(8MB 内存分配)，耗费约 8 毫秒。 注意若首次内存分配成功之后，再对同一个 <code>key</code> 调用 SETRANGE 操作，无须再重新内存。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>对小(small)的字符串，平摊复杂度O(1)。(关于什么字符串是”小”的，请参考 <em>APPEND</em> 命令)否则为O(M)， <code>M</code> 为 <code>value</code> 参数的长度。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>被 SETRANGE 修改之后，字符串的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"># 对非空字符串进行 SETRANGE</span><br><span class="line"></span><br><span class="line">redis&gt; SET greeting &quot;hello world&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SETRANGE greeting 6 &quot;Redis&quot;</span><br><span class="line">(integer) 11</span><br><span class="line"></span><br><span class="line">redis&gt; GET greeting</span><br><span class="line">&quot;hello Redis&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对空字符串&#x2F;不存在的 key 进行 SETRANGE</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS empty_string</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SETRANGE empty_string 5 &quot;Redis!&quot;   # 对不存在的 key 使用 SETRANGE</span><br><span class="line">(integer) 11</span><br><span class="line"></span><br><span class="line">redis&gt; GET empty_string                   # 空白处被&quot;\x00&quot;填充</span><br><span class="line">&quot;\x00\x00\x00\x00\x00Redis!&quot;</span><br></pre></td></tr></table></figure>

<h3 id="模式-1"><a href="#模式-1" class="headerlink" title="模式"></a>模式</h3><p>因为有了 SETRANGE 和 <em>GETRANGE</em> 命令，你可以将 Redis 字符串用作具有O(1)随机访问时间的线性数组，这在很多真实用例中都是非常快速且高效的储存方式，具体请参考 <em>APPEND</em> 命令的『模式：时间序列』部分。</p>
<h2 id="22-STRLEN"><a href="#22-STRLEN" class="headerlink" title="22.STRLEN"></a>22.STRLEN</h2><p><strong>STRLEN key</strong></p>
<p>返回 <code>key</code> 所储存的字符串值的长度。</p>
<p>当 <code>key</code> 储存的不是字符串值时，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>字符串值的长度。当 <code>key</code> 不存在时，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"># 获取字符串的长度</span><br><span class="line"></span><br><span class="line">redis&gt; SET mykey &quot;Hello world&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; STRLEN mykey</span><br><span class="line">(integer) 11</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 不存在的 key 长度为 0</span><br><span class="line"></span><br><span class="line">redis&gt; STRLEN nonexisting</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h1 id="三、Hash（哈希表）"><a href="#三、Hash（哈希表）" class="headerlink" title="三、Hash（哈希表）"></a>三、Hash（哈希表）</h1><h2 id="1-HDEL"><a href="#1-HDEL" class="headerlink" title="1.HDEL"></a>1.HDEL</h2><p><strong>HDEL key field [field …]</strong></p>
<p>删除哈希表 <code>key</code> 中的一个或多个指定域，不存在的域将被忽略。</p>
<p>在Redis2.4以下的版本里， HDEL 每次只能删除单个域，如果你需要在一个原子时间内删除多个域，请将命令包含在 <em>MULTI</em> / <em>EXEC</em> 块内。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 为要删除的域的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被成功移除的域的数量，不包括被忽略的域。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"># 测试数据</span><br><span class="line"></span><br><span class="line">redis&gt; HGETALL abbr</span><br><span class="line">1) &quot;a&quot;</span><br><span class="line">2) &quot;apple&quot;</span><br><span class="line">3) &quot;b&quot;</span><br><span class="line">4) &quot;banana&quot;</span><br><span class="line">5) &quot;c&quot;</span><br><span class="line">6) &quot;cat&quot;</span><br><span class="line">7) &quot;d&quot;</span><br><span class="line">8) &quot;dog&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 删除单个域</span><br><span class="line"></span><br><span class="line">redis&gt; HDEL abbr a</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 删除不存在的域</span><br><span class="line"></span><br><span class="line">redis&gt; HDEL abbr not-exists-field</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 删除多个域</span><br><span class="line"></span><br><span class="line">redis&gt; HDEL abbr b c</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; HGETALL abbr</span><br><span class="line">1) &quot;d&quot;</span><br><span class="line">2) &quot;dog&quot;</span><br></pre></td></tr></table></figure>

<h2 id="2-HEXISTS"><a href="#2-HEXISTS" class="headerlink" title="2.HEXISTS"></a>2.HEXISTS</h2><p><strong>HEXISTS key field</strong></p>
<p>查看哈希表 <code>key</code> 中，给定域 <code>field</code> 是否存在。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果哈希表含有给定域，返回 <code>1</code> 。如果哈希表不含有给定域，或 <code>key</code> 不存在，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HEXISTS phone myphone</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; HSET phone myphone nokia-1110</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HEXISTS phone myphone</span><br><span class="line">(integer) 1</span><br></pre></td></tr></table></figure>

<h2 id="3-HGET"><a href="#3-HGET" class="headerlink" title="3.HGET"></a>3.HGET</h2><p><strong>HGET key field</strong></p>
<p>返回哈希表 <code>key</code> 中给定域 <code>field</code> 的值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>给定域的值。当给定域不存在或是给定 <code>key</code> 不存在时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"># 域存在</span><br><span class="line"></span><br><span class="line">redis&gt; HSET site redis redis.com</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HGET site redis</span><br><span class="line">&quot;redis.com&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 域不存在</span><br><span class="line"></span><br><span class="line">redis&gt; HGET site mysql</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="4-HGETALL"><a href="#4-HGETALL" class="headerlink" title="4.HGETALL"></a>4.HGETALL</h2><p><strong>HGETALL key</strong></p>
<p>返回哈希表 <code>key</code> 中，所有的域和值。</p>
<p>在返回值里，紧跟每个域名(field name)之后是域的值(value)，所以返回值的长度是哈希表大小的两倍。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为哈希表的大小。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>以列表形式返回哈希表的域和域的值。若 <code>key</code> 不存在，返回空列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HSET people jack &quot;Jack Sparrow&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HSET people gump &quot;Forrest Gump&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HGETALL people</span><br><span class="line">1) &quot;jack&quot;          # 域</span><br><span class="line">2) &quot;Jack Sparrow&quot;  # 值</span><br><span class="line">3) &quot;gump&quot;</span><br><span class="line">4) &quot;Forrest Gump&quot;</span><br></pre></td></tr></table></figure>

<h2 id="5-HINCRBY"><a href="#5-HINCRBY" class="headerlink" title="5.HINCRBY"></a>5.HINCRBY</h2><p><strong>HINCRBY key field increment</strong></p>
<p>为哈希表 <code>key</code> 中的域 <code>field</code> 的值加上增量 <code>increment</code> 。</p>
<p>增量也可以为负数，相当于对给定域进行减法操作。</p>
<p>如果 <code>key</code> 不存在，一个新的哈希表被创建并执行 HINCRBY 命令。</p>
<p>如果域 <code>field</code> 不存在，那么在执行命令前，域的值被初始化为 <code>0</code> 。</p>
<p>对一个储存字符串值的域 <code>field</code> 执行 HINCRBY 命令将造成一个错误。</p>
<p>本操作的值被限制在 64 位(bit)有符号数字表示之内。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行 HINCRBY 命令之后，哈希表 <code>key</code> 中域 <code>field</code> 的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"># increment 为正数</span><br><span class="line"></span><br><span class="line">redis&gt; HEXISTS counter page_view    # 对空域进行设置</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; HINCRBY counter page_view 200</span><br><span class="line">(integer) 200</span><br><span class="line"></span><br><span class="line">redis&gt; HGET counter page_view</span><br><span class="line">&quot;200&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># increment 为负数</span><br><span class="line"></span><br><span class="line">redis&gt; HGET counter page_view</span><br><span class="line">&quot;200&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; HINCRBY counter page_view -50</span><br><span class="line">(integer) 150</span><br><span class="line"></span><br><span class="line">redis&gt; HGET counter page_view</span><br><span class="line">&quot;150&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 尝试对字符串值的域执行HINCRBY命令</span><br><span class="line"></span><br><span class="line">redis&gt; HSET myhash string hello,world       # 设定一个字符串值</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HGET myhash string</span><br><span class="line">&quot;hello,world&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; HINCRBY myhash string 1              # 命令执行失败，错误。</span><br><span class="line">(error) ERR hash value is not an integer</span><br><span class="line"></span><br><span class="line">redis&gt; HGET myhash string                   # 原值不变</span><br><span class="line">&quot;hello,world&quot;</span><br></pre></td></tr></table></figure>

<h2 id="6-HINCRBYFLOAT"><a href="#6-HINCRBYFLOAT" class="headerlink" title="6.HINCRBYFLOAT"></a>6.HINCRBYFLOAT</h2><p><strong>HINCRBYFLOAT key field increment</strong></p>
<p>为哈希表 <code>key</code> 中的域 <code>field</code> 加上浮点数增量 <code>increment</code> 。</p>
<p>如果哈希表中没有域 <code>field</code> ，那么 HINCRBYFLOAT 会先将域 <code>field</code> 的值设为 <code>0</code> ，然后再执行加法操作。</p>
<p>如果键 <code>key</code> 不存在，那么 HINCRBYFLOAT 会先创建一个哈希表，再创建域 <code>field</code> ，最后再执行加法操作。</p>
<p>当以下任意一个条件发生时，返回一个错误：</p>
<ul>
<li>域 <code>field</code> 的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存，所以它们都属于字符串类型）</li>
<li>域 <code>field</code> 当前的值或给定的增量 <code>increment</code> 不能解释(parse)为双精度浮点数(double precision floating point number)</li>
</ul>
<p>HINCRBYFLOAT 命令的详细功能和 <em>INCRBYFLOAT</em> 命令类似，请查看 <em>INCRBYFLOAT</em> 命令获取更多相关信息。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行加法操作之后 <code>field</code> 域的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"># 值和增量都是普通小数</span><br><span class="line"></span><br><span class="line">redis&gt; HSET mykey field 10.50</span><br><span class="line">(integer) 1</span><br><span class="line">redis&gt; HINCRBYFLOAT mykey field 0.1</span><br><span class="line">&quot;10.6&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 值和增量都是指数符号</span><br><span class="line"></span><br><span class="line">redis&gt; HSET mykey field 5.0e3</span><br><span class="line">(integer) 0</span><br><span class="line">redis&gt; HINCRBYFLOAT mykey field 2.0e2</span><br><span class="line">&quot;5200&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对不存在的键执行 HINCRBYFLOAT</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS price</span><br><span class="line">(integer) 0</span><br><span class="line">redis&gt; HINCRBYFLOAT price milk 3.5</span><br><span class="line">&quot;3.5&quot;</span><br><span class="line">redis&gt; HGETALL price</span><br><span class="line">1) &quot;milk&quot;</span><br><span class="line">2) &quot;3.5&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对不存在的域进行 HINCRBYFLOAT</span><br><span class="line"></span><br><span class="line">redis&gt; HGETALL price</span><br><span class="line">1) &quot;milk&quot;</span><br><span class="line">2) &quot;3.5&quot;</span><br><span class="line">redis&gt; HINCRBYFLOAT price coffee 4.5   # 新增 coffee 域</span><br><span class="line">&quot;4.5&quot;</span><br><span class="line">redis&gt; HGETALL price</span><br><span class="line">1) &quot;milk&quot;</span><br><span class="line">2) &quot;3.5&quot;</span><br><span class="line">3) &quot;coffee&quot;</span><br><span class="line">4) &quot;4.5&quot;</span><br></pre></td></tr></table></figure>

<h2 id="7-HKEYS"><a href="#7-HKEYS" class="headerlink" title="7.HKEYS"></a>7.HKEYS</h2><p><strong>HKEYS key</strong></p>
<p>返回哈希表 <code>key</code> 中的所有域。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为哈希表的大小。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个包含哈希表中所有域的表。当 <code>key</code> 不存在时，返回一个空表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"># 哈希表非空</span><br><span class="line"></span><br><span class="line">redis&gt; HMSET website google www.google.com yahoo www.yahoo.com</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; HKEYS website</span><br><span class="line">1) &quot;google&quot;</span><br><span class="line">2) &quot;yahoo&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 空哈希表&#x2F;key不存在</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS fake_key</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; HKEYS fake_key</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="8-HLEN"><a href="#8-HLEN" class="headerlink" title="8.HLEN"></a>8.HLEN</h2><p><strong>HLEN key</strong></p>
<p>返回哈希表 <code>key</code> 中域的数量。</p>
<ul>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>哈希表中域的数量。当 <code>key</code> 不存在时，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HSET db redis redis.com</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HSET db mysql mysql.com</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HLEN db</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; HSET db mongodb mongodb.org</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HLEN db</span><br><span class="line">(integer) 3</span><br></pre></td></tr></table></figure>

<h2 id="9-HMGET"><a href="#9-HMGET" class="headerlink" title="9.HMGET"></a>9.HMGET</h2><p><strong>HMGET key field [field …]</strong></p>
<p>返回哈希表 <code>key</code> 中，一个或多个给定域的值。</p>
<p>如果给定的域不存在于哈希表，那么返回一个 <code>nil</code> 值。</p>
<p>因为不存在的 <code>key</code> 被当作一个空哈希表来处理，所以对一个不存在的 <code>key</code> 进行 HMGET 操作将返回一个只带有 <code>nil</code> 值的表。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为给定域的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个包含多个给定域的关联值的表，表值的排列顺序和给定域参数的请求顺序一样。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HMSET pet dog &quot;doudou&quot; cat &quot;nounou&quot;    # 一次设置多个域</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; HMGET pet dog cat fake_pet             # 返回值的顺序和传入参数的顺序一样</span><br><span class="line">1) &quot;doudou&quot;</span><br><span class="line">2) &quot;nounou&quot;</span><br><span class="line">3) (nil)                                      # 不存在的域返回nil值</span><br></pre></td></tr></table></figure>

<h2 id="10-HMSET"><a href="#10-HMSET" class="headerlink" title="10.HMSET"></a>10.HMSET</h2><p><strong>HMSET key field value [field value …]</strong></p>
<p>同时将多个 <code>field-value</code> (域-值)对设置到哈希表 <code>key</code> 中。</p>
<p>此命令会覆盖哈希表中已存在的域。</p>
<p>如果 <code>key</code> 不存在，一个空哈希表被创建并执行 HMSET 操作。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为 <code>field-value</code> 对的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果命令执行成功，返回 <code>OK</code> 。当 <code>key</code> 不是哈希表(hash)类型时，返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HMSET website google www.google.com yahoo www.yahoo.com</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; HGET website google</span><br><span class="line">&quot;www.google.com&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; HGET website yahoo</span><br><span class="line">&quot;www.yahoo.com&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-HSET"><a href="#11-HSET" class="headerlink" title="11.HSET"></a>11.HSET</h2><p><strong>HSET key field value</strong></p>
<p>将哈希表 <code>key</code> 中的域 <code>field</code> 的值设为 <code>value</code> 。</p>
<p>如果 <code>key</code> 不存在，一个新的哈希表被创建并进行 HSET 操作。</p>
<p>如果域 <code>field</code> 已经存在于哈希表中，旧值将被覆盖。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果 <code>field</code> 是哈希表中的一个新建域，并且值设置成功，返回 <code>1</code> 。如果哈希表中域 <code>field</code> 已经存在且旧值已被新值覆盖，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HSET website google &quot;www.g.cn&quot;       # 设置一个新域</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HSET website google &quot;www.google.com&quot; # 覆盖一个旧域</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="12-HSETNX"><a href="#12-HSETNX" class="headerlink" title="12.HSETNX"></a>12.HSETNX</h2><p><strong>HSETNX key field value</strong></p>
<p>将哈希表 <code>key</code> 中的域 <code>field</code> 的值设置为 <code>value</code> ，当且仅当域 <code>field</code> 不存在。</p>
<p>若域 <code>field</code> 已经存在，该操作无效。</p>
<p>如果 <code>key</code> 不存在，一个新哈希表被创建并执行 HSETNX命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>设置成功，返回 <code>1</code> 。如果给定域已经存在且没有操作被执行，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; HSETNX nosql key-value-store redis</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; HSETNX nosql key-value-store redis       # 操作无效，域 key-value-store 已存在</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="13-HVALS"><a href="#13-HVALS" class="headerlink" title="13.HVALS"></a>13.HVALS</h2><p><strong>HVALS key</strong></p>
<p>返回哈希表 <code>key</code> 中所有域的值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为哈希表的大小。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个包含哈希表中所有值的表。当 <code>key</code> 不存在时，返回一个空表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"># 非空哈希表</span><br><span class="line"></span><br><span class="line">redis&gt; HMSET website google www.google.com yahoo www.yahoo.com</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; HVALS website</span><br><span class="line">1) &quot;www.google.com&quot;</span><br><span class="line">2) &quot;www.yahoo.com&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 空哈希表&#x2F;不存在的key</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS not_exists</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; HVALS not_exists</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="14-HSCAN"><a href="#14-HSCAN" class="headerlink" title="14.HSCAN"></a>14.HSCAN</h2><p><strong>HSCAN key cursor [MATCH pattern] [COUNT count]</strong></p>
<p>具体信息请参考 [<em>SCAN</em> 命令。</p>
<h1 id="四、List（列表）"><a href="#四、List（列表）" class="headerlink" title="四、List（列表）"></a>四、List（列表）</h1><h2 id="1-BLPOP"><a href="#1-BLPOP" class="headerlink" title="1.BLPOP"></a>1.BLPOP</h2><p><strong>BLPOP key [key …] timeout</strong></p>
<p>BLPOP 是列表的阻塞式(blocking)弹出原语。</p>
<p>它是 <em>LPOP</em> 命令的阻塞版本，当给定列表内没有任何元素可供弹出的时候，连接将被 [BLPOP 命令阻塞，直到等待超时或发现可弹出元素为止。</p>
<p>当给定多个 <code>key</code> 参数时，按参数 <code>key</code> 的先后顺序依次检查各个列表，弹出第一个非空列表的头元素。</p>
<p><strong>非阻塞行为</strong></p>
<p>当 BLPOP 被调用时，如果给定 <code>key</code> 内至少有一个非空列表，那么弹出遇到的第一个非空列表的头元素，并和被弹出元素所属的列表的名字一起，组成结果返回给调用者。</p>
<p>当存在多个给定 <code>key</code> 时， BLPOP 按给定 <code>key</code> 参数排列的先后顺序，依次检查各个列表。</p>
<p>假设现在有 <code>job</code> 、 <code>command</code> 和 <code>request</code> 三个列表，其中 <code>job</code> 不存在， <code>command</code> 和 <code>request</code> 都持有非空列表。考虑以下命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">BLPOP job command request 0</span><br></pre></td></tr></table></figure>

<p>BLPOP 保证返回的元素来自 <code>command</code> ，因为它是按”查找 <code>job</code> -&gt; 查找 <code>command</code> -&gt; 查找 <code>request</code> “这样的顺序，第一个找到的非空列表。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DEL job command request           # 确保key都被删除</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH command &quot;update system...&quot;  # 为command列表增加一个值</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH request &quot;visit page&quot;        # 为request列表增加一个值</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; BLPOP job command request 0       # job 列表为空，被跳过，紧接着 command 列表的第一个元素被弹出。</span><br><span class="line">1) &quot;command&quot;                             # 弹出元素所属的列表</span><br><span class="line">2) &quot;update system...&quot;                    # 弹出元素所属的值</span><br></pre></td></tr></table></figure>

<p><strong>阻塞行为</strong></p>
<p>如果所有给定 <code>key</code> 都不存在或包含空列表，那么 [BLPOP 命令将阻塞连接，直到等待超时，或有另一个客户端对给定 <code>key</code> 的任意一个执行 <em>LPUSH*l 或 *RPUSH</em> 命令为止。</p>
<p>超时参数 <code>timeout</code> 接受一个以秒为单位的数字作为值。超时参数设为 <code>0</code> 表示阻塞时间可以无限期延长(block indefinitely) 。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; EXISTS job                # 确保两个 key 都不存在</span><br><span class="line">(integer) 0</span><br><span class="line">redis&gt; EXISTS command</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; BLPOP job command 300     # 因为key一开始不存在，所以操作会被阻塞，直到另一客户端对 job 或者 command 列表进行 PUSH 操作。</span><br><span class="line">1) &quot;job&quot;                         # 这里被 push 的是 job</span><br><span class="line">2) &quot;do my home work&quot;             # 被弹出的值</span><br><span class="line">(26.26s)                         # 等待的秒数</span><br><span class="line"></span><br><span class="line">redis&gt; BLPOP job command 5       # 等待超时的情况</span><br><span class="line">(nil)</span><br><span class="line">(5.66s)                          # 等待的秒数</span><br></pre></td></tr></table></figure>

<p><strong>相同的key被多个客户端同时阻塞</strong></p>
<p>相同的 <code>key</code> 可以被多个客户端同时阻塞。</p>
<p>不同的客户端被放进一个队列中，按『先阻塞先服务』(first-BLPOP，first-served)的顺序为 <code>key</code> 执行 [BLPOP 命令。</p>
<p><strong>在MULTI/EXEC事务中的BLPOP</strong></p>
<p>BLPOP 可以用于流水线(pipline,批量地发送多个命令并读入多个回复)，但把它用在 <em>MULTI</em> / <em>EXEC</em> 块当中没有意义。因为这要求整个服务器被阻塞以保证块执行时的原子性，该行为阻止了其他客户端执行 <em>LPUSH*l 或 *RPUSH</em> 命令。</p>
<p>因此，一个被包裹在 <em>MULTI</em> / <em>EXEC</em> 块内的 BLPOP 命令，行为表现得就像 <em>LPOP</em> 一样，对空列表返回 <code>nil</code> ，对非空列表弹出列表元素，不进行任何阻塞操作。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"># 对非空列表进行操作</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH job programming</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; BLPOP job 30</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC           # 不阻塞，立即返回</span><br><span class="line">1) 1) &quot;job&quot;</span><br><span class="line">   2) &quot;programming&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对空列表进行操作</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN job      # 空列表</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; BLPOP job 30</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC         # 不阻塞，立即返回</span><br><span class="line">1) (nil)</span><br></pre></td></tr></table></figure>

<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果列表为空，返回一个 <code>nil</code> 。否则，返回一个含有两个元素的列表，第一个元素是被弹出元素所属的 <code>key</code> ，第二个元素是被弹出元素的值。</p>
</li>
</ul>
<h3 id="模式：事件提醒"><a href="#模式：事件提醒" class="headerlink" title="模式：事件提醒"></a>模式：事件提醒</h3><p>有时候，为了等待一个新元素到达数据中，需要使用轮询的方式对数据进行探查。</p>
<p>另一种更好的方式是，使用系统提供的阻塞原语，在新元素到达时立即进行处理，而新元素还没到达时，就一直阻塞住，避免轮询占用资源。</p>
<p>对于 Redis ，我们似乎需要一个阻塞版的 <em>SPOP</em> 命令，但实际上，使用 BLPOP 或者 <em>BRPOP</em> 就能很好地解决这个问题。</p>
<p>使用元素的客户端(消费者)可以执行类似以下的代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">LOOP forever</span><br><span class="line">    WHILE SPOP(key) returns elements</span><br><span class="line">        ... process elements ...</span><br><span class="line">    END</span><br><span class="line">    BRPOP helper_key</span><br><span class="line">END</span><br></pre></td></tr></table></figure>

<p>添加元素的客户端(消费者)则执行以下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MULTI</span><br><span class="line">    SADD key element</span><br><span class="line">    LPUSH helper_key x</span><br><span class="line">EXEC</span><br></pre></td></tr></table></figure>

<h2 id="2-BRPOP"><a href="#2-BRPOP" class="headerlink" title="2.BRPOP"></a>2.BRPOP</h2><p><strong>BRPOP key [key …] timeout</strong></p>
<p>BRPOP 是列表的阻塞式(blocking)弹出原语。</p>
<p>它是 <em>RPOP</em> 命令的阻塞版本，当给定列表内没有任何元素可供弹出的时候，连接将被 BRPOP 命令阻塞，直到等待超时或发现可弹出元素为止。</p>
<p>当给定多个 <code>key</code> 参数时，按参数 <code>key</code> 的先后顺序依次检查各个列表，弹出第一个非空列表的尾部元素。</p>
<p>关于阻塞操作的更多信息，请查看 <em>BLPOP</em> 命令，[BRPOP 除了弹出元素的位置和 <em>BLPOP</em> 不同之外，其他表现一致。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>假如在指定时间内没有任何元素被弹出，则返回一个 <code>nil</code> 和等待时长。反之，返回一个含有两个元素的列表，第一个元素是被弹出元素所属的 <code>key</code> ，第二个元素是被弹出元素的值。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; LLEN course</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH course algorithm001</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH course c++101</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; BRPOP course 30</span><br><span class="line">1) &quot;course&quot;             # 弹出元素的 key</span><br><span class="line">2) &quot;c++101&quot;             # 弹出元素的值</span><br></pre></td></tr></table></figure>

<h2 id="3-BRPOPLPUSH"><a href="#3-BRPOPLPUSH" class="headerlink" title="3.BRPOPLPUSH"></a>3.BRPOPLPUSH</h2><p><strong>BRPOPLPUSH source destination timeout</strong></p>
<p>BRPOPLPUSH 是 <em>RPOPLPUSH</em> 的阻塞版本，当给定列表 <code>source</code> 不为空时， BRPOPLPUSH 的表现和 <em>RPOPLPUSH</em> 一样。</p>
<p>当列表 <code>source</code> 为空时， BRPOPLPUSH 命令将阻塞连接，直到等待超时，或有另一个客户端对 <code>source</code> 执行 <em>LPUSH*l 或 *RPUSH</em> 命令为止。</p>
<p>超时参数 <code>timeout</code> 接受一个以秒为单位的数字作为值。超时参数设为 <code>0</code> 表示阻塞时间可以无限期延长(block indefinitely) 。</p>
<p>更多相关信息，请参考 <em>RPOPLPUSH</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>假如在指定时间内没有任何元素被弹出，则返回一个 <code>nil</code> 和等待时长。反之，返回一个含有两个元素的列表，第一个元素是被弹出元素的值，第二个元素是等待时长。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"># 非空列表</span><br><span class="line"></span><br><span class="line">redis&gt; BRPOPLPUSH msg reciver 500</span><br><span class="line">&quot;hello moto&quot;                        # 弹出元素的值</span><br><span class="line">(3.38s)                             # 等待时长</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN reciver</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE reciver 0 0</span><br><span class="line">1) &quot;hello moto&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 空列表</span><br><span class="line"></span><br><span class="line">redis&gt; BRPOPLPUSH msg reciver 1</span><br><span class="line">(nil)</span><br><span class="line">(1.34s)</span><br></pre></td></tr></table></figure>

<h3 id="模式：安全队列"><a href="#模式：安全队列" class="headerlink" title="模式：安全队列"></a>模式：安全队列</h3><p>参考 <em>RPOPLPUSH</em> 命令的『安全队列』模式。</p>
<h3 id="模式：循环列表"><a href="#模式：循环列表" class="headerlink" title="模式：循环列表"></a>模式：循环列表</h3><p>参考 <em>RPOPLPUSH</em> 命令的『循环列表』模式。</p>
<h2 id="4-LINDEX"><a href="#4-LINDEX" class="headerlink" title="4.LINDEX"></a>4.LINDEX</h2><p><strong>LINDEX key index</strong></p>
<p>返回列表 <code>key</code> 中，下标为 <code>index</code> 的元素。</p>
<p>下标(index)参数 <code>start</code> 和 <code>stop</code> 都以 <code>0</code> 为底，也就是说，以 <code>0</code> 表示列表的第一个元素，以 <code>1</code> 表示列表的第二个元素，以此类推。</p>
<p>你也可以使用负数下标，以 <code>-1</code> 表示列表的最后一个元素， <code>-2</code> 表示列表的倒数第二个元素，以此类推。</p>
<p>如果 <code>key</code> 不是列表类型，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为到达下标 <code>index</code> 过程中经过的元素数量。因此，对列表的头元素和尾元素执行 LINDEXl 命令，复杂度为O(1)。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>列表中下标为 <code>index</code> 的元素。如果 <code>index</code> 参数的值不在列表的区间范围内(out of range)，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; LPUSH mylist &quot;World&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH mylist &quot;Hello&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LINDEX mylist 0</span><br><span class="line">&quot;Hello&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LINDEX mylist -1</span><br><span class="line">&quot;World&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LINDEX mylist 3        # index不在 mylist 的区间范围内</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="5-LINSERT"><a href="#5-LINSERT" class="headerlink" title="5.LINSERT"></a>5.LINSERT</h2><p><strong>LINSERT key BEFORE|AFTER pivot value</strong></p>
<p>将值 <code>value</code> 插入到列表 <code>key</code> 当中，位于值 <code>pivot</code> 之前或之后。</p>
<p>当 <code>pivot</code> 不存在于列表 <code>key</code> 时，不执行任何操作。</p>
<p>当 <code>key</code> 不存在时， <code>key</code> 被视为空列表，不执行任何操作。</p>
<p>如果 <code>key</code> 不是列表类型，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 为寻找 <code>pivot</code> 过程中经过的元素数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>如果命令执行成功，返回插入操作完成之后，列表的长度。如果没有找到 <code>pivot</code> ，返回 <code>-1</code> 。如果 <code>key</code> 不存在或为空列表，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; RPUSH mylist &quot;Hello&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH mylist &quot;World&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LINSERT mylist BEFORE &quot;World&quot; &quot;There&quot;</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE mylist 0 -1</span><br><span class="line">1) &quot;Hello&quot;</span><br><span class="line">2) &quot;There&quot;</span><br><span class="line">3) &quot;World&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对一个非空列表插入，查找一个不存在的 pivot</span><br><span class="line"></span><br><span class="line">redis&gt; LINSERT mylist BEFORE &quot;go&quot; &quot;let&#39;s&quot;</span><br><span class="line">(integer) -1                                    # 失败</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对一个空列表执行 LINSERT 命令</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS fake_list</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; LINSERT fake_list BEFORE &quot;nono&quot; &quot;gogogog&quot;</span><br><span class="line">(integer) 0                                      # 失败</span><br></pre></td></tr></table></figure>

<h2 id="6-LLEN"><a href="#6-LLEN" class="headerlink" title="6.LLEN"></a>6.LLEN</h2><p><strong>LLEN key</strong></p>
<p>返回列表 <code>key</code> 的长度。</p>
<p>如果 <code>key</code> 不存在，则 <code>key</code> 被解释为一个空列表，返回 <code>0</code> .</p>
<p>如果 <code>key</code> 不是列表类型，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>列表 <code>key</code> 的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 空列表</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN job</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 非空列表</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH job &quot;cook food&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH job &quot;have lunch&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN job</span><br><span class="line">(integer) 2</span><br></pre></td></tr></table></figure>

<h2 id="7-LPOP"><a href="#7-LPOP" class="headerlink" title="7.LPOP"></a>7.LPOP</h2><p><strong>LPOP key</strong></p>
<p>移除并返回列表 <code>key</code> 的头元素。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>列表的头元素。当 <code>key</code> 不存在时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; LLEN course</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH course algorithm001</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH course c++101</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LPOP course  # 移除头元素</span><br><span class="line">&quot;algorithm001&quot;</span><br></pre></td></tr></table></figure>

<h2 id="8-LPUSH"><a href="#8-LPUSH" class="headerlink" title="8.LPUSH"></a>8.LPUSH</h2><p><strong>LPUSH key value [value …]</strong></p>
<p>将一个或多个值 <code>value</code> 插入到列表 <code>key</code> 的表头</p>
<p>如果有多个 <code>value</code> 值，那么各个 <code>value</code> 值按从左到右的顺序依次插入到表头： 比如说，对空列表 <code>mylist</code> 执行命令 <code>LPUSH mylist a b c</code> ，列表的值将是 <code>c b a</code> ，这等同于原子性地执行 <code>LPUSH mylist a</code> 、 <code>LPUSH mylist b</code> 和 <code>LPUSH mylist c</code> 三个命令。</p>
<p>如果 <code>key</code> 不存在，一个空列表会被创建并执行 LPUSHl 操作。</p>
<p>当 <code>key</code> 存在但不是列表类型时，返回一个错误。</p>
<p>在Redis 2.4版本以前的 LPUSHl 命令，都只接受单个 <code>value</code> 值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行 LPUSHl 命令后，列表的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 加入单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH languages python</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 加入重复元素</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH languages python</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE languages 0 -1     # 列表允许重复元素</span><br><span class="line">1) &quot;python&quot;</span><br><span class="line">2) &quot;python&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 加入多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH mylist a b c</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE mylist 0 -1</span><br><span class="line">1) &quot;c&quot;</span><br><span class="line">2) &quot;b&quot;</span><br><span class="line">3) &quot;a&quot;</span><br></pre></td></tr></table></figure>

<h2 id="9-LPUSHX"><a href="#9-LPUSHX" class="headerlink" title="9.LPUSHX"></a>9.LPUSHX</h2><p><strong>LPUSHX key value</strong></p>
<p>将值 <code>value</code> 插入到列表 <code>key</code> 的表头，当且仅当 <code>key</code> 存在并且是一个列表。</p>
<p>和 <em>LPUSH</em> 命令相反，当 <code>key</code> 不存在时， LPUSHX 命令什么也不做。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>LPUSHX 命令执行之后，表的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># 对空列表执行 LPUSHX</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN greet                       # greet 是一个空列表</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSHX greet &quot;hello&quot;             # 尝试 LPUSHX，失败，因为列表为空</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对非空列表执行 LPUSHX</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH greet &quot;hello&quot;              # 先用 LPUSH 创建一个有一个元素的列表</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSHX greet &quot;good morning&quot;      # 这次 LPUSHX 执行成功</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE greet 0 -1</span><br><span class="line">1) &quot;good morning&quot;</span><br><span class="line">2) &quot;hello&quot;</span><br></pre></td></tr></table></figure>

<h2 id="10-LRANGE"><a href="#10-LRANGE" class="headerlink" title="10.LRANGE"></a>10.LRANGE</h2><p><strong>LRANGE key start stop</strong></p>
<p>返回列表 <code>key</code> 中指定区间内的元素，区间以偏移量 <code>start</code> 和 <code>stop</code> 指定。</p>
<p>下标(index)参数 <code>start</code> 和 <code>stop</code> 都以 <code>0</code> 为底，也就是说，以 <code>0</code> 表示列表的第一个元素，以 <code>1</code> 表示列表的第二个元素，以此类推。</p>
<p>你也可以使用负数下标，以 <code>-1</code> 表示列表的最后一个元素， <code>-2</code> 表示列表的倒数第二个元素，以此类推。</p>
<p><strong>注意LRANGE命令和编程语言区间函数的区别</strong></p>
<p>假如你有一个包含一百个元素的列表，对该列表执行 <code>LRANGE list 0 10</code> ，结果是一个包含11个元素的列表，这表明 <code>stop</code> 下标也在 LRANGE 命令的取值范围之内(闭区间)，这和某些语言的区间函数可能不一致，比如Ruby的 <code>Range.new</code> 、 <code>Array#slice</code> 和Python的 <code>range()</code> 函数。</p>
<p><strong>超出范围的下标</strong></p>
<p>超出范围的下标值不会引起错误。</p>
<p>如果 <code>start</code> 下标比列表的最大下标 <code>end</code> ( <code>LLEN list</code> 减去 <code>1</code> )还要大，那么 LRANGE 返回一个空列表。</p>
<p>如果 <code>stop</code> 下标比 <code>end</code> 下标还要大，Redis将 <code>stop</code> 的值设置为 <code>end</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(S+N)， <code>S</code> 为偏移量 <code>start</code> ， <code>N</code> 为指定区间内元素的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>一个列表，包含指定区间内的元素。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; RPUSH fp-language lisp</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE fp-language 0 0</span><br><span class="line">1) &quot;lisp&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH fp-language scheme</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE fp-language 0 1</span><br><span class="line">1) &quot;lisp&quot;</span><br><span class="line">2) &quot;scheme&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-LREM"><a href="#11-LREM" class="headerlink" title="11.LREM"></a>11.LREM</h2><p><strong>LREM key count value</strong></p>
<p>根据参数 <code>count</code> 的值，移除列表中与参数 <code>value</code> 相等的元素。</p>
<p><code>count</code> 的值可以是以下几种：</p>
<ul>
<li><p><code>count &gt; 0</code> : 从表头开始向表尾搜索，移除与 <code>value</code> 相等的元素，数量为 <code>count</code> 。</p>
</li>
<li><p><code>count &lt; 0</code> : 从表尾开始向表头搜索，移除与 <code>value</code> 相等的元素，数量为 <code>count</code> 的绝对值。</p>
</li>
<li><p><code>count = 0</code> : 移除表中所有与 <code>value</code> 相等的值。</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为列表的长度。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>被移除元素的数量。因为不存在的 <code>key</code> 被视作空表(empty list)，所以当 <code>key</code> 不存在时， LREM 命令总是返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"># 先创建一个表，内容排列是</span><br><span class="line"># morning hello morning helllo morning</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH greet &quot;morning&quot;</span><br><span class="line">(integer) 1</span><br><span class="line">redis&gt; LPUSH greet &quot;hello&quot;</span><br><span class="line">(integer) 2</span><br><span class="line">redis&gt; LPUSH greet &quot;morning&quot;</span><br><span class="line">(integer) 3</span><br><span class="line">redis&gt; LPUSH greet &quot;hello&quot;</span><br><span class="line">(integer) 4</span><br><span class="line">redis&gt; LPUSH greet &quot;morning&quot;</span><br><span class="line">(integer) 5</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE greet 0 4         # 查看所有元素</span><br><span class="line">1) &quot;morning&quot;</span><br><span class="line">2) &quot;hello&quot;</span><br><span class="line">3) &quot;morning&quot;</span><br><span class="line">4) &quot;hello&quot;</span><br><span class="line">5) &quot;morning&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LREM greet 2 morning     # 移除从表头到表尾，最先发现的两个 morning</span><br><span class="line">(integer) 2                     # 两个元素被移除</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN greet               # 还剩 3 个元素</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE greet 0 2</span><br><span class="line">1) &quot;hello&quot;</span><br><span class="line">2) &quot;hello&quot;</span><br><span class="line">3) &quot;morning&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LREM greet -1 morning    # 移除从表尾到表头，第一个 morning</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN greet               # 剩下两个元素</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE greet 0 1</span><br><span class="line">1) &quot;hello&quot;</span><br><span class="line">2) &quot;hello&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LREM greet 0 hello      # 移除表中所有 hello</span><br><span class="line">(integer) 2                    # 两个 hello 被移除</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN greet</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="12-LSET"><a href="#12-LSET" class="headerlink" title="12.LSET"></a>12.LSET</h2><p><strong>LSET key index value</strong></p>
<p>将列表 <code>key</code> 下标为 <code>index</code> 的元素的值设置为 <code>value</code> 。</p>
<p>当 <code>index</code> 参数超出范围，或对一个空列表( <code>key</code> 不存在)进行 LSET 时，返回一个错误。</p>
<p>关于列表下标的更多信息，请参考 <em>LINDEX</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>对头元素或尾元素进行 LSET 操作，复杂度为 O(1)。其他情况下，为 O(N)， <code>N</code> 为列表的长度。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>操作成功返回 <code>ok</code> ，否则返回错误信息。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"># 对空列表(key 不存在)进行 LSET</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS list</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; LSET list 0 item</span><br><span class="line">(error) ERR no such key</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 对非空列表进行 LSET</span><br><span class="line"></span><br><span class="line">redis&gt; LPUSH job &quot;cook food&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE job 0 0</span><br><span class="line">1) &quot;cook food&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LSET job 0 &quot;play game&quot;</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE job  0 0</span><br><span class="line">1) &quot;play game&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># index 超出范围</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN list                    # 列表长度为 1</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; LSET list 3 &#39;out of range&#39;</span><br><span class="line">(error) ERR index out of range</span><br></pre></td></tr></table></figure>

<h2 id="13-LTRIM"><a href="#13-LTRIM" class="headerlink" title="13.LTRIM"></a>13.LTRIM</h2><p><strong>LTRIM key start stop</strong></p>
<p>对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。</p>
<p>举个例子，执行命令 <code>LTRIM list 0 2</code> ，表示只保留列表 <code>list</code> 的前三个元素，其余元素全部删除。</p>
<p>下标(index)参数 <code>start</code> 和 <code>stop</code> 都以 <code>0</code> 为底，也就是说，以 <code>0</code> 表示列表的第一个元素，以 <code>1</code> 表示列表的第二个元素，以此类推。</p>
<p>你也可以使用负数下标，以 <code>-1</code> 表示列表的最后一个元素， <code>-2</code> 表示列表的倒数第二个元素，以此类推。</p>
<p>当 <code>key</code> 不是列表类型时，返回一个错误。</p>
<p>LTRIM 命令通常和 <em>LPUSH</em> 命令或 <em>RPUSH</em> 命令配合使用，举个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LPUSH log newest_log</span><br><span class="line">LTRIM log 0 99</span><br></pre></td></tr></table></figure>

<p>这个例子模拟了一个日志程序，每次将最新日志 <code>newest_log</code> 放到 <code>log</code> 列表中，并且只保留最新的 <code>100</code> 项。注意当这样使用 <code>LTRIM</code> 命令时，时间复杂度是O(1)，因为平均情况下，每次只有一个元素被移除。</p>
<p><strong>注意LTRIM命令和编程语言区间函数的区别</strong></p>
<p>假如你有一个包含一百个元素的列表 <code>list</code> ，对该列表执行 <code>LTRIM list 0 10</code> ，结果是一个包含11个元素的列表，这表明 <code>stop</code> 下标也在 [LTRIM 命令的取值范围之内(闭区间)，这和某些语言的区间函数可能不一致，比如Ruby的 <code>Range.new</code> 、 <code>Array#slice</code> 和Python的 <code>range()</code> 函数。</p>
<p><strong>超出范围的下标</strong></p>
<p>超出范围的下标值不会引起错误。</p>
<p>如果 <code>start</code> 下标比列表的最大下标 <code>end</code> ( <code>LLEN list</code> 减去 <code>1</code> )还要大，或者 <code>start &gt; stop</code> ， LTRIM 返回一个空列表(因为 [LTRIM 已经将整个列表清空)。</p>
<p>如果 <code>stop</code> 下标比 <code>end</code> 下标还要大，Redis将 <code>stop</code> 的值设置为 <code>end</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 为被移除的元素的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>命令执行成功时，返回 <code>ok</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"># 情况 1： 常见情况， start 和 stop 都在列表的索引范围之内</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1       # alpha 是一个包含 5 个字符串的列表</span><br><span class="line">1) &quot;h&quot;</span><br><span class="line">2) &quot;e&quot;</span><br><span class="line">3) &quot;l&quot;</span><br><span class="line">4) &quot;l&quot;</span><br><span class="line">5) &quot;o&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LTRIM alpha 1 -1        # 删除 alpha 列表索引为 0 的元素</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1       # &quot;h&quot; 被删除了</span><br><span class="line">1) &quot;e&quot;</span><br><span class="line">2) &quot;l&quot;</span><br><span class="line">3) &quot;l&quot;</span><br><span class="line">4) &quot;o&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 情况 2： stop 比列表的最大下标还要大</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">redis&gt; LTRIM alpha 1 10086     # 保留 alpha 列表索引 1 至索引 10086 上的元素</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1       # 只有索引 0 上的元素 &quot;e&quot; 被删除了，其他元素还在</span><br><span class="line">1) &quot;l&quot;</span><br><span class="line">2) &quot;l&quot;</span><br><span class="line">3) &quot;o&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 情况 3： start 和 stop 都比列表的最大下标要大，并且 start &lt; stop</span><br><span class="line"></span><br><span class="line">redis&gt; LTRIM alpha 10086 123321</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1        # 列表被清空</span><br><span class="line">(empty list or set)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 情况 4： start 和 stop 都比列表的最大下标要大，并且 start &gt; stop</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH new-alpha &quot;h&quot; &quot;e&quot; &quot;l&quot; &quot;l&quot; &quot;o&quot;     # 重新建立一个新列表</span><br><span class="line">(integer) 5</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE new-alpha 0 -1</span><br><span class="line">1) &quot;h&quot;</span><br><span class="line">2) &quot;e&quot;</span><br><span class="line">3) &quot;l&quot;</span><br><span class="line">4) &quot;l&quot;</span><br><span class="line">5) &quot;o&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LTRIM new-alpha 123321 10086    # 执行 LTRIM</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE new-alpha 0 -1           # 同样被清空</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="14-RPOP"><a href="#14-RPOP" class="headerlink" title="14.RPOP"></a>14.RPOP</h2><p><strong>RPOP key</strong></p>
<p>移除并返回列表 <code>key</code> 的尾元素。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>列表的尾元素。当 <code>key</code> 不存在时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; RPUSH mylist &quot;one&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH mylist &quot;two&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH mylist &quot;three&quot;</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; RPOP mylist           # 返回被弹出的元素</span><br><span class="line">&quot;three&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE mylist 0 -1    # 列表剩下的元素</span><br><span class="line">1) &quot;one&quot;</span><br><span class="line">2) &quot;two&quot;</span><br></pre></td></tr></table></figure>

<h2 id="15-RPOPLPUSH"><a href="#15-RPOPLPUSH" class="headerlink" title="15.RPOPLPUSH"></a>15.RPOPLPUSH</h2><p><strong>RPOPLPUSH source destination</strong></p>
<p>命令 RPOPLPUSH 在一个原子时间内，执行以下两个动作：</p>
<ul>
<li>将列表 <code>source</code> 中的最后一个元素(尾元素)弹出，并返回给客户端。</li>
<li>将 <code>source</code> 弹出的元素插入到列表 <code>destination</code> ，作为 <code>destination</code> 列表的的头元素。</li>
</ul>
<p>举个例子，你有两个列表 <code>source</code> 和 <code>destination</code> ， <code>source</code> 列表有元素 <code>a, b, c</code> ， <code>destination</code> 列表有元素 <code>x, y, z</code> ，执行 <code>RPOPLPUSH source destination</code> 之后， <code>source</code> 列表包含元素 <code>a, b</code> ， <code>destination</code> 列表包含元素 <code>c, x, y, z</code> ，并且元素 <code>c</code> 会被返回给客户端。</p>
<p>如果 <code>source</code> 不存在，值 <code>nil</code> 被返回，并且不执行其他动作。</p>
<p>如果 <code>source</code> 和 <code>destination</code> 相同，则列表中的表尾元素被移动到表头，并返回该元素，可以把这种特殊情况视作列表的旋转(rotation)操作。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>被弹出的元素。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"># source 和 destination 不同</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1         # 查看所有元素</span><br><span class="line">1) &quot;a&quot;</span><br><span class="line">2) &quot;b&quot;</span><br><span class="line">3) &quot;c&quot;</span><br><span class="line">4) &quot;d&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RPOPLPUSH alpha reciver   # 执行一次 RPOPLPUSH 看看</span><br><span class="line">&quot;d&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1</span><br><span class="line">1) &quot;a&quot;</span><br><span class="line">2) &quot;b&quot;</span><br><span class="line">3) &quot;c&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE reciver 0 -1</span><br><span class="line">1) &quot;d&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RPOPLPUSH alpha reciver   # 再执行一次，证实 RPOP 和 LPUSH 的位置正确</span><br><span class="line">&quot;c&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE alpha 0 -1</span><br><span class="line">1) &quot;a&quot;</span><br><span class="line">2) &quot;b&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE reciver 0 -1</span><br><span class="line">1) &quot;c&quot;</span><br><span class="line">2) &quot;d&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># source 和 destination 相同</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE number 0 -1</span><br><span class="line">1) &quot;1&quot;</span><br><span class="line">2) &quot;2&quot;</span><br><span class="line">3) &quot;3&quot;</span><br><span class="line">4) &quot;4&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RPOPLPUSH number number</span><br><span class="line">&quot;4&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE number 0 -1           # 4 被旋转到了表头</span><br><span class="line">1) &quot;4&quot;</span><br><span class="line">2) &quot;1&quot;</span><br><span class="line">3) &quot;2&quot;</span><br><span class="line">4) &quot;3&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; RPOPLPUSH number number</span><br><span class="line">&quot;3&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE number 0 -1           # 这次是 3 被旋转到了表头</span><br><span class="line">1) &quot;3&quot;</span><br><span class="line">2) &quot;4&quot;</span><br><span class="line">3) &quot;1&quot;</span><br><span class="line">4) &quot;2&quot;</span><br></pre></td></tr></table></figure>

<h3 id="模式：-安全的队列"><a href="#模式：-安全的队列" class="headerlink" title="模式： 安全的队列"></a>模式： 安全的队列</h3><p>Redis的列表经常被用作队列(queue)，用于在不同程序之间有序地交换消息(message)。一个客户端通过 [<em>LPUSH*l 命令将消息放入队列中，而另一个客户端通过 *RPOP</em> 或者 <em>BRPOP</em> 命令取出队列中等待时间最长的消息。</p>
<p>不幸的是，上面的队列方法是『不安全』的，因为在这个过程中，一个客户端可能在取出一个消息之后崩溃，而未处理完的消息也就因此丢失。</p>
<p>使用 RPOPLPUSH 命令(或者它的阻塞版本 <em>BRPOPLPUSH</em> )可以解决这个问题：因为它不仅返回一个消息，同时还将这个消息添加到另一个备份列表当中，如果一切正常的话，当一个客户端完成某个消息的处理之后，可以用 <em>LREM</em> 命令将这个消息从备份表删除。</p>
<p>最后，还可以添加一个客户端专门用于监视备份表，它自动地将超过一定处理时限的消息重新放入队列中去(负责处理该消息的客户端可能已经崩溃)，这样就不会丢失任何消息了。</p>
<h3 id="模式：循环列表-1"><a href="#模式：循环列表-1" class="headerlink" title="模式：循环列表"></a>模式：循环列表</h3><p>通过使用相同的 <code>key</code> 作为 RPOPLPUSH 命令的两个参数，客户端可以用一个接一个地获取列表元素的方式，取得列表的所有元素，而不必像 <em>LRANGE</em> 命令那样一下子将所有列表元素都从服务器传送到客户端中(两种方式的总复杂度都是 O(N))。</p>
<p>以上的模式甚至在以下的两个情况下也能正常工作：</p>
<ul>
<li>有多个客户端同时对同一个列表进行旋转(rotating)，它们获取不同的元素，直到所有元素都被读取完，之后又从头开始。</li>
<li>有客户端在向列表尾部(右边)添加新元素。</li>
</ul>
<p>这个模式使得我们可以很容易实现这样一类系统：有 N 个客户端，需要连续不断地对一些元素进行处理，而且处理的过程必须尽可能地快。一个典型的例子就是服务器的监控程序：它们需要在尽可能短的时间内，并行地检查一组网站，确保它们的可访问性。</p>
<p>注意，使用这个模式的客户端是易于扩展(scala)且安全(reliable)的，因为就算接收到元素的客户端失败，元素还是保存在列表里面，不会丢失，等到下个迭代来临的时候，别的客户端又可以继续处理这些元素了。</p>
<h2 id="16-RPUSH"><a href="#16-RPUSH" class="headerlink" title="16.RPUSH"></a>16.RPUSH</h2><p><strong>RPUSH key value [value …]</strong></p>
<p>将一个或多个值 <code>value</code> 插入到列表 <code>key</code> 的表尾(最右边)。</p>
<p>如果有多个 <code>value</code> 值，那么各个 <code>value</code> 值按从左到右的顺序依次插入到表尾：比如对一个空列表 <code>mylist</code> 执行 <code>RPUSH mylist a b c</code> ，得出的结果列表为 <code>a b c</code> ，等同于执行命令 <code>RPUSH mylist a</code> 、 <code>RPUSH mylist b</code> 、 <code>RPUSH mylist c</code> 。</p>
<p>如果 <code>key</code> 不存在，一个空列表会被创建并执行 RPUSH 操作。</p>
<p>当 <code>key</code> 存在但不是列表类型时，返回一个错误。</p>
<p>在 Redis 2.4 版本以前的 RPUSH 命令，都只接受单个 <code>value</code> 值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行 RPUSH 操作后，表的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 添加单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH languages c</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加重复元素</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH languages c</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE languages 0 -1 # 列表允许重复元素</span><br><span class="line">1) &quot;c&quot;</span><br><span class="line">2) &quot;c&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH mylist a b c</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE mylist 0 -1</span><br><span class="line">1) &quot;a&quot;</span><br><span class="line">2) &quot;b&quot;</span><br><span class="line">3) &quot;c&quot;</span><br></pre></td></tr></table></figure>

<h2 id="17-RPUSHX"><a href="#17-RPUSHX" class="headerlink" title="17.RPUSHX"></a>17.RPUSHX</h2><p><strong>RPUSHX key value</strong></p>
<p>将值 <code>value</code> 插入到列表 <code>key</code> 的表尾，当且仅当 <code>key</code> 存在并且是一个列表。</p>
<p>和 <em>RPUSH</em> 命令相反，当 <code>key</code> 不存在时， RPUSHX 命令什么也不做。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>RPUSHX 命令执行之后，表的长度。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># key不存在</span><br><span class="line"></span><br><span class="line">redis&gt; LLEN greet</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSHX greet &quot;hello&quot;     # 对不存在的 key 进行 RPUSHX，PUSH 失败。</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># key 存在且是一个非空列表</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSH greet &quot;hi&quot;         # 先用 RPUSH 插入一个元素</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; RPUSHX greet &quot;hello&quot;     # greet 现在是一个列表类型，RPUSHX 操作成功。</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; LRANGE greet 0 -1</span><br><span class="line">1) &quot;hi&quot;</span><br><span class="line">2) &quot;hello&quot;</span><br></pre></td></tr></table></figure>

<h1 id="五、Set（集合）"><a href="#五、Set（集合）" class="headerlink" title="五、Set（集合）"></a>五、Set（集合）</h1><h2 id="1-SADD"><a href="#1-SADD" class="headerlink" title="1.SADD"></a>1.SADD</h2><p><strong>SADD key member [member …]</strong></p>
<p>将一个或多个 <code>member</code> 元素加入到集合 <code>key</code> 当中，已经存在于集合的 <code>member</code> 元素将被忽略。</p>
<p>假如 <code>key</code> 不存在，则创建一个只包含 <code>member</code> 元素作成员的集合。</p>
<p>当 <code>key</code> 不是集合类型时，返回一个错误。</p>
<p>在Redis2.4版本以前， SADD 只接受单个 <code>member</code> 值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 是被添加的元素的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被添加到集合中的新元素的数量，不包括被忽略的元素。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"># 添加单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; SADD bbs &quot;discuz.net&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加重复元素</span><br><span class="line"></span><br><span class="line">redis&gt; SADD bbs &quot;discuz.net&quot;</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; SADD bbs &quot;tianya.cn&quot; &quot;groups.google.com&quot;</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS bbs</span><br><span class="line">1) &quot;discuz.net&quot;</span><br><span class="line">2) &quot;groups.google.com&quot;</span><br><span class="line">3) &quot;tianya.cn&quot;</span><br></pre></td></tr></table></figure>

<h2 id="2-SCARD"><a href="#2-SCARD" class="headerlink" title="2.SCARD"></a>2.SCARD</h2><p><strong>SCARD key</strong></p>
<p>返回集合 <code>key</code> 的基数(集合中元素的数量)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>集合的基数。当 <code>key</code> 不存在时，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SADD tool pc printer phone</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; SCARD tool   # 非空集合</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; DEL tool</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SCARD tool   # 空集合</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="3-SDIFF"><a href="#3-SDIFF" class="headerlink" title="3.SDIFF"></a>3.SDIFF</h2><p><strong>SDIFF key [key …]</strong></p>
<p>返回一个集合的全部成员，该集合是所有给定集合之间的差集。</p>
<p>不存在的 <code>key</code> 被视为空集。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 是所有给定集合的成员数量之和。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>交集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS peter&#39;s_movies</span><br><span class="line">1) &quot;bet man&quot;</span><br><span class="line">2) &quot;start war&quot;</span><br><span class="line">3) &quot;2012&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS joe&#39;s_movies</span><br><span class="line">1) &quot;hi, lady&quot;</span><br><span class="line">2) &quot;Fast Five&quot;</span><br><span class="line">3) &quot;2012&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SDIFF peter&#39;s_movies joe&#39;s_movies</span><br><span class="line">1) &quot;bet man&quot;</span><br><span class="line">2) &quot;start war&quot;</span><br></pre></td></tr></table></figure>

<h2 id="4-SDIFFSTORE"><a href="#4-SDIFFSTORE" class="headerlink" title="4.SDIFFSTORE"></a>4.SDIFFSTORE</h2><p><strong>SDIFFSTORE destination key [key …]</strong></p>
<p>这个命令的作用和 <em>SDIFF</em> 类似，但它将结果保存到 <code>destination</code> 集合，而不是简单地返回结果集。</p>
<p>如果 <code>destination</code> 集合已经存在，则将其覆盖。</p>
<p><code>destination</code> 可以是 <code>key</code> 本身。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 是所有给定集合的成员数量之和。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>结果集中的元素数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS joe&#39;s_movies</span><br><span class="line">1) &quot;hi, lady&quot;</span><br><span class="line">2) &quot;Fast Five&quot;</span><br><span class="line">3) &quot;2012&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS peter&#39;s_movies</span><br><span class="line">1) &quot;bet man&quot;</span><br><span class="line">2) &quot;start war&quot;</span><br><span class="line">3) &quot;2012&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SDIFFSTORE joe_diff_peter joe&#39;s_movies peter&#39;s_movies</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS joe_diff_peter</span><br><span class="line">1) &quot;hi, lady&quot;</span><br><span class="line">2) &quot;Fast Five&quot;</span><br></pre></td></tr></table></figure>

<h2 id="5-SINTER"><a href="#5-SINTER" class="headerlink" title="5.SINTER"></a>5.SINTER</h2><p><strong>SINTER key [key …]</strong></p>
<p>返回一个集合的全部成员，该集合是所有给定集合的交集。</p>
<p>不存在的 <code>key</code> 被视为空集。</p>
<p>当给定集合当中有一个空集时，结果也为空集(根据集合运算定律)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N * M)， <code>N</code> 为给定集合当中基数最小的集合， <code>M</code> 为给定集合的个数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>交集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS group_1</span><br><span class="line">1) &quot;LI LEI&quot;</span><br><span class="line">2) &quot;TOM&quot;</span><br><span class="line">3) &quot;JACK&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS group_2</span><br><span class="line">1) &quot;HAN MEIMEI&quot;</span><br><span class="line">2) &quot;JACK&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SINTER group_1 group_2</span><br><span class="line">1) &quot;JACK&quot;</span><br></pre></td></tr></table></figure>

<h2 id="6-SINTERSTORE"><a href="#6-SINTERSTORE" class="headerlink" title="6.SINTERSTORE"></a>6.SINTERSTORE</h2><p><strong>SINTERSTORE destination key [key …]</strong></p>
<p>这个命令类似于 <em>SINTER</em> 命令，但它将结果保存到 <code>destination</code> 集合，而不是简单地返回结果集。</p>
<p>如果 <code>destination</code> 集合已经存在，则将其覆盖。</p>
<p><code>destination</code> 可以是 <code>key</code> 本身。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N * M)， <code>N</code> 为给定集合当中基数最小的集合， <code>M</code> 为给定集合的个数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>结果集中的成员数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS songs</span><br><span class="line">1) &quot;good bye joe&quot;</span><br><span class="line">2) &quot;hello,peter&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS my_songs</span><br><span class="line">1) &quot;good bye joe&quot;</span><br><span class="line">2) &quot;falling&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SINTERSTORE song_interset songs my_songs</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS song_interset</span><br><span class="line">1) &quot;good bye joe&quot;</span><br></pre></td></tr></table></figure>

<h2 id="7-SISMEMBER"><a href="#7-SISMEMBER" class="headerlink" title="7.SISMEMBER"></a>7.SISMEMBER</h2><p><strong>SISMEMBER key member</strong></p>
<p>判断 <code>member</code> 元素是否集合 <code>key</code> 的成员。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>如果 <code>member</code> 元素是集合的成员，返回 <code>1</code> 。如果 <code>member</code> 元素不是集合的成员，或 <code>key</code> 不存在，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS joe&#39;s_movies</span><br><span class="line">1) &quot;hi, lady&quot;</span><br><span class="line">2) &quot;Fast Five&quot;</span><br><span class="line">3) &quot;2012&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SISMEMBER joe&#39;s_movies &quot;bet man&quot;</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SISMEMBER joe&#39;s_movies &quot;Fast Five&quot;</span><br><span class="line">(integer) 1</span><br></pre></td></tr></table></figure>

<h2 id="8-SMEMBERS"><a href="#8-SMEMBERS" class="headerlink" title="8.SMEMBERS"></a>8.SMEMBERS</h2><p><strong>SMEMBERS key</strong></p>
<p>返回集合 <code>key</code> 中的所有成员。</p>
<p>不存在的 <code>key</code> 被视为空集合。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 为集合的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>集合中的所有成员。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"># key 不存在或集合为空</span><br><span class="line"></span><br><span class="line">redis&gt; EXISTS not_exists_key</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS not_exists_key</span><br><span class="line">(empty list or set)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 非空集合</span><br><span class="line"></span><br><span class="line">redis&gt; SADD language Ruby Python Clojure</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS language</span><br><span class="line">1) &quot;Python&quot;</span><br><span class="line">2) &quot;Ruby&quot;</span><br><span class="line">3) &quot;Clojure&quot;</span><br></pre></td></tr></table></figure>

<h2 id="9-SMOVE"><a href="#9-SMOVE" class="headerlink" title="9.SMOVE"></a>9.SMOVE</h2><p><strong>SMOVE source destination member</strong></p>
<p>将 <code>member</code> 元素从 <code>source</code> 集合移动到 <code>destination</code> 集合。</p>
<p>SMOVE 是原子性操作。</p>
<p>如果 <code>source</code> 集合不存在或不包含指定的 <code>member</code> 元素，则 SMOVE 命令不执行任何操作，仅返回 <code>0</code> 。否则， <code>member</code> 元素从 <code>source</code> 集合中被移除，并添加到 <code>destination</code> 集合中去。</p>
<p>当 <code>destination</code> 集合已经包含 <code>member</code> 元素时， SMOVE 命令只是简单地将 <code>source</code> 集合中的 <code>member</code> 元素删除。</p>
<p>当 <code>source</code> 或 <code>destination</code> 不是集合类型时，返回一个错误。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>如果 <code>member</code> 元素被成功移除，返回 <code>1</code> 。如果 <code>member</code> 元素不是 <code>source</code> 集合的成员，并且没有任何操作对 <code>destination</code> 集合执行，那么返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS songs</span><br><span class="line">1) &quot;Billie Jean&quot;</span><br><span class="line">2) &quot;Believe Me&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS my_songs</span><br><span class="line">(empty list or set)</span><br><span class="line"></span><br><span class="line">redis&gt; SMOVE songs my_songs &quot;Believe Me&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS songs</span><br><span class="line">1) &quot;Billie Jean&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS my_songs</span><br><span class="line">1) &quot;Believe Me&quot;</span><br></pre></td></tr></table></figure>

<h2 id="10-SPOP"><a href="#10-SPOP" class="headerlink" title="10.SPOP"></a>10.SPOP</h2><p><strong>SPOP key</strong></p>
<p>移除并返回集合中的一个随机元素。</p>
<p>如果只想获取一个随机元素，但不想该元素从集合中被移除的话，可以使用 <em>SRANDMEMBER</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被移除的随机元素。当 <code>key</code> 不存在或 <code>key</code> 是空集时，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS db</span><br><span class="line">1) &quot;MySQL&quot;</span><br><span class="line">2) &quot;MongoDB&quot;</span><br><span class="line">3) &quot;Redis&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SPOP db</span><br><span class="line">&quot;Redis&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS db</span><br><span class="line">1) &quot;MySQL&quot;</span><br><span class="line">2) &quot;MongoDB&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SPOP db</span><br><span class="line">&quot;MySQL&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS db</span><br><span class="line">1) &quot;MongoDB&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-SRANDMEMBER"><a href="#11-SRANDMEMBER" class="headerlink" title="11.SRANDMEMBER"></a>11.SRANDMEMBER</h2><p><strong>SRANDMEMBER key [count]</strong></p>
<p>如果命令执行时，只提供了 <code>key</code> 参数，那么返回集合中的一个随机元素。</p>
<p>从 Redis 2.6 版本开始， SRANDMEMBER 命令接受可选的 <code>count</code> 参数：</p>
<ul>
<li>如果 <code>count</code> 为正数，且小于集合基数，那么命令返回一个包含 <code>count</code> 个元素的数组，数组中的元素<strong>各不相同</strong>。如果 <code>count</code> 大于等于集合基数，那么返回整个集合。</li>
<li>如果 <code>count</code> 为负数，那么命令返回一个数组，数组中的元素<strong>可能会重复出现多次</strong>，而数组的长度为 <code>count</code> 的绝对值。</li>
</ul>
<p>该操作和 <em>SPOP</em> 相似，但 <em>SPOP</em> 将随机元素从集合中移除并返回，而 SRANDMEMBER 则仅仅返回随机元素，而不对集合进行任何改动。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>只提供 <code>key</code> 参数时为 O(1) 。如果提供了 <code>count</code> 参数，那么为 O(N) ，N 为返回数组的元素个数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>只提供 <code>key</code> 参数时，返回一个元素；如果集合为空，返回 <code>nil</code> 。如果提供了 <code>count</code> 参数，那么返回一个数组；如果集合为空，返回空数组。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"># 添加元素</span><br><span class="line"></span><br><span class="line">redis&gt; SADD fruit apple banana cherry</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line"># 只给定 key 参数，返回一个随机元素</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit</span><br><span class="line">&quot;cherry&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit</span><br><span class="line">&quot;apple&quot;</span><br><span class="line"></span><br><span class="line"># 给定 3 为 count 参数，返回 3 个随机元素</span><br><span class="line"># 每个随机元素都不相同</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit 3</span><br><span class="line">1) &quot;apple&quot;</span><br><span class="line">2) &quot;banana&quot;</span><br><span class="line">3) &quot;cherry&quot;</span><br><span class="line"></span><br><span class="line"># 给定 -3 为 count 参数，返回 3 个随机元素</span><br><span class="line"># 元素可能会重复出现多次</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit -3</span><br><span class="line">1) &quot;banana&quot;</span><br><span class="line">2) &quot;cherry&quot;</span><br><span class="line">3) &quot;apple&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit -3</span><br><span class="line">1) &quot;apple&quot;</span><br><span class="line">2) &quot;apple&quot;</span><br><span class="line">3) &quot;cherry&quot;</span><br><span class="line"></span><br><span class="line"># 如果 count 是整数，且大于等于集合基数，那么返回整个集合</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit 10</span><br><span class="line">1) &quot;apple&quot;</span><br><span class="line">2) &quot;banana&quot;</span><br><span class="line">3) &quot;cherry&quot;</span><br><span class="line"></span><br><span class="line"># 如果 count 是负数，且 count 的绝对值大于集合的基数</span><br><span class="line"># 那么返回的数组的长度为 count 的绝对值</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER fruit -10</span><br><span class="line">1) &quot;banana&quot;</span><br><span class="line">2) &quot;apple&quot;</span><br><span class="line">3) &quot;banana&quot;</span><br><span class="line">4) &quot;cherry&quot;</span><br><span class="line">5) &quot;apple&quot;</span><br><span class="line">6) &quot;apple&quot;</span><br><span class="line">7) &quot;cherry&quot;</span><br><span class="line">8) &quot;apple&quot;</span><br><span class="line">9) &quot;apple&quot;</span><br><span class="line">10) &quot;banana&quot;</span><br><span class="line"></span><br><span class="line"># SRANDMEMBER 并不会修改集合内容</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS fruit</span><br><span class="line">1) &quot;apple&quot;</span><br><span class="line">2) &quot;cherry&quot;</span><br><span class="line">3) &quot;banana&quot;</span><br><span class="line"></span><br><span class="line"># 集合为空时返回 nil 或者空数组</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER not-exists</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line">redis&gt; SRANDMEMBER not-eixsts 10</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="12-SREM"><a href="#12-SREM" class="headerlink" title="12.SREM"></a>12.SREM</h2><p><strong>SREM key member [member …]</strong></p>
<p>移除集合 <code>key</code> 中的一个或多个 <code>member</code> 元素，不存在的 <code>member</code> 元素会被忽略。</p>
<p>当 <code>key</code> 不是集合类型，返回一个错误。</p>
<p>在 Redis 2.4 版本以前， SREM 只接受单个 <code>member</code> 值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 为给定 <code>member</code> 元素的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被成功移除的元素的数量，不包括被忽略的元素。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"># 测试数据</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS languages</span><br><span class="line">1) &quot;c&quot;</span><br><span class="line">2) &quot;lisp&quot;</span><br><span class="line">3) &quot;python&quot;</span><br><span class="line">4) &quot;ruby&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; SREM languages ruby</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除不存在元素</span><br><span class="line"></span><br><span class="line">redis&gt; SREM languages non-exists-language</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; SREM languages lisp python c</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS languages</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="13-SUNION"><a href="#13-SUNION" class="headerlink" title="13.SUNION"></a>13.SUNION</h2><p><strong>SUNION key [key …]</strong></p>
<p>返回一个集合的全部成员，该集合是所有给定集合的并集。</p>
<p>不存在的 <code>key</code> 被视为空集。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 是所有给定集合的成员数量之和。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>并集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS songs</span><br><span class="line">1) &quot;Billie Jean&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS my_songs</span><br><span class="line">1) &quot;Believe Me&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SUNION songs my_songs</span><br><span class="line">1) &quot;Billie Jean&quot;</span><br><span class="line">2) &quot;Believe Me&quot;</span><br></pre></td></tr></table></figure>

<h2 id="14-SUNIONSTORE"><a href="#14-SUNIONSTORE" class="headerlink" title="14.SUNIONSTORE"></a>14.SUNIONSTORE</h2><p><strong>SUNIONSTORE destination key [key …]</strong></p>
<p>这个命令类似于 <em>SUNION</em> 命令，但它将结果保存到 <code>destination</code> 集合，而不是简单地返回结果集。</p>
<p>如果 <code>destination</code> 已经存在，则将其覆盖。</p>
<p><code>destination</code> 可以是 <code>key</code> 本身。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)， <code>N</code> 是所有给定集合的成员数量之和。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>结果集中的元素数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SMEMBERS NoSQL</span><br><span class="line">1) &quot;MongoDB&quot;</span><br><span class="line">2) &quot;Redis&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS SQL</span><br><span class="line">1) &quot;sqlite&quot;</span><br><span class="line">2) &quot;MySQL&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SUNIONSTORE db NoSQL SQL</span><br><span class="line">(integer) 4</span><br><span class="line"></span><br><span class="line">redis&gt; SMEMBERS db</span><br><span class="line">1) &quot;MySQL&quot;</span><br><span class="line">2) &quot;sqlite&quot;</span><br><span class="line">3) &quot;MongoDB&quot;</span><br><span class="line">4) &quot;Redis&quot;</span><br></pre></td></tr></table></figure>

<h2 id="15-SSCAN"><a href="#15-SSCAN" class="headerlink" title="15.SSCAN"></a>15.SSCAN</h2><p><strong>SSCAN key cursor [MATCH pattern] [COUNT count]</strong></p>
<p>详细信息请参考 [<em>SCAN</em> 命令。</p>
<h1 id="六、SortedSet（有序集合）"><a href="#六、SortedSet（有序集合）" class="headerlink" title="六、SortedSet（有序集合）"></a>六、SortedSet（有序集合）</h1><h2 id="1-ZADD"><a href="#1-ZADD" class="headerlink" title="1.ZADD"></a>1.ZADD</h2><p><strong>ZADD key score member [[score member] [score member] …]</strong></p>
<p>将一个或多个 <code>member</code> 元素及其 <code>score</code> 值加入到有序集 <code>key</code> 当中。</p>
<p>如果某个 <code>member</code> 已经是有序集的成员，那么更新这个 <code>member</code> 的 <code>score</code> 值，并通过重新插入这个 <code>member</code> 元素，来保证该 <code>member</code> 在正确的位置上。</p>
<p><code>score</code> 值可以是整数值或双精度浮点数。</p>
<p>如果 <code>key</code> 不存在，则创建一个空的有序集并执行 ZADD 操作。</p>
<p>当 <code>key</code> 存在但不是有序集类型时，返回一个错误。</p>
<p>对有序集的更多介绍请参见 sorted set 。</p>
<p>在 Redis 2.4 版本以前， ZADD 每次只能添加一个元素。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(M*log(N))， <code>N</code> 是有序集的基数， <code>M</code> 为成功添加的新成员的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被成功添加的新成员的数量，不包括那些被更新的、已经存在的成员。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"># 添加单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; ZADD page_rank 10 google.com</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; ZADD page_rank 9 baidu.com 8 bing.com</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES</span><br><span class="line">1) &quot;bing.com&quot;</span><br><span class="line">2) &quot;8&quot;</span><br><span class="line">3) &quot;baidu.com&quot;</span><br><span class="line">4) &quot;9&quot;</span><br><span class="line">5) &quot;google.com&quot;</span><br><span class="line">6) &quot;10&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加已存在元素，且 score 值不变</span><br><span class="line"></span><br><span class="line">redis&gt; ZADD page_rank 10 google.com</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES  # 没有改变</span><br><span class="line">1) &quot;bing.com&quot;</span><br><span class="line">2) &quot;8&quot;</span><br><span class="line">3) &quot;baidu.com&quot;</span><br><span class="line">4) &quot;9&quot;</span><br><span class="line">5) &quot;google.com&quot;</span><br><span class="line">6) &quot;10&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 添加已存在元素，但是改变 score 值</span><br><span class="line"></span><br><span class="line">redis&gt; ZADD page_rank 6 bing.com</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES  # bing.com 元素的 score 值被改变</span><br><span class="line">1) &quot;bing.com&quot;</span><br><span class="line">2) &quot;6&quot;</span><br><span class="line">3) &quot;baidu.com&quot;</span><br><span class="line">4) &quot;9&quot;</span><br><span class="line">5) &quot;google.com&quot;</span><br><span class="line">6) &quot;10&quot;</span><br></pre></td></tr></table></figure>

<h2 id="2-ZCARD"><a href="#2-ZCARD" class="headerlink" title="2.ZCARD"></a>2.ZCARD</h2><p><strong>ZCARD key</strong></p>
<p>返回有序集 <code>key</code> 的基数。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>当 <code>key</code> 存在且是有序集类型时，返回有序集的基数。当 <code>key</code> 不存在时，返回 <code>0</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">redis &gt; ZADD salary 2000 tom    # 添加一个成员</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZCARD salary</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZADD salary 5000 jack   # 再添加一个成员</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZCARD salary</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis &gt; EXISTS non_exists_key   # 对不存在的 key 进行 ZCARD 操作</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis &gt; ZCARD non_exists_key</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="3-ZCOUNT"><a href="#3-ZCOUNT" class="headerlink" title="3.ZCOUNT"></a>3.ZCOUNT</h2><p><strong>ZCOUNT key min max</strong></p>
<p>返回有序集 <code>key</code> 中， <code>score</code> 值在 <code>min</code> 和 <code>max</code> 之间(默认包括 <code>score</code> 值等于 <code>min</code> 或 <code>max</code> )的成员的数量。</p>
<p>关于参数 <code>min</code> 和 <code>max</code> 的详细使用方法，请参考 <em>ZRANGEBYSCORE</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数， <code>M</code> 为值在 <code>min</code> 和 <code>max</code> 之间的元素的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p><code>score</code> 值在 <code>min</code> 和 <code>max</code> 之间的成员的数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES    # 测试数据</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;tom&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZCOUNT salary 2000 5000          # 计算薪水在 2000-5000 之间的人数</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis&gt; ZCOUNT salary 3000 5000          # 计算薪水在 3000-5000 之间的人数</span><br><span class="line">(integer) 2</span><br></pre></td></tr></table></figure>

<h2 id="4-ZINCRBY"><a href="#4-ZINCRBY" class="headerlink" title="4.ZINCRBY"></a>4.ZINCRBY</h2><p><strong>ZINCRBY key increment member</strong></p>
<p>为有序集 <code>key</code> 的成员 <code>member</code> 的 <code>score</code> 值加上增量 <code>increment</code> 。</p>
<p>可以通过传递一个负数值 <code>increment</code> ，让 <code>score</code> 减去相应的值，比如 <code>ZINCRBY key -5 member</code> ，就是让 <code>member</code> 的 <code>score</code> 值减去 <code>5</code> 。</p>
<p>当 <code>key</code> 不存在，或 <code>member</code> 不是 <code>key</code> 的成员时， <code>ZINCRBY key increment member</code> 等同于 <code>ZADD key increment member</code> 。</p>
<p>当 <code>key</code> 不是有序集类型时，返回一个错误。</p>
<p><code>score</code> 值可以是整数值或双精度浮点数。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N))</p>
</li>
<li><p><strong>返回值:</strong></p>
<p><code>member</code> 成员的新 <code>score</code> 值，以字符串形式表示。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZSCORE salary tom</span><br><span class="line">&quot;2000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZINCRBY salary 2000 tom   # tom 加薪啦！</span><br><span class="line">&quot;4000&quot;</span><br></pre></td></tr></table></figure>

<h2 id="5-ZRANGE"><a href="#5-ZRANGE" class="headerlink" title="5.ZRANGE"></a>5.ZRANGE</h2><p><strong>ZRANGE key start stop [WITHSCORES]</strong></p>
<p>返回有序集 <code>key</code> 中，指定区间内的成员。</p>
<p>其中成员的位置按 <code>score</code> 值递增(从小到大)来排序。</p>
<p>具有相同 <code>score</code> 值的成员按字典序(lexicographical order )来排列。</p>
<p>如果你需要成员按 <code>score</code> 值递减(从大到小)来排列，请使用 <em>ZREVRANGE</em> 命令。</p>
<p>下标参数 <code>start</code> 和 <code>stop</code> 都以 <code>0</code> 为底，也就是说，以 <code>0</code> 表示有序集第一个成员，以 <code>1</code> 表示有序集第二个成员，以此类推。</p>
<p>你也可以使用负数下标，以 <code>-1</code> 表示最后一个成员， <code>-2</code> 表示倒数第二个成员，以此类推。</p>
<p>超出范围的下标并不会引起错误。</p>
<p>比如说，当 <code>start</code> 的值比有序集的最大下标还要大，或是 <code>start &gt; stop</code> 时， ZRANGE 命令只是简单地返回一个空列表。</p>
<p>另一方面，假如 <code>stop</code> 参数的值比有序集的最大下标还要大，那么 Redis 将 <code>stop</code> 当作最大下标来处理。</p>
<p>可以通过使用 <code>WITHSCORES</code> 选项，来让成员和它的 <code>score</code> 值一并返回，返回列表以 <code>value1,score1, ..., valueN,scoreN</code> 的格式表示。</p>
<p>客户端库可能会返回一些更复杂的数据类型，比如数组、元组等。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数，而 <code>M</code> 为结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>指定区间内，带有 <code>score</code> 值(可选)的有序集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">redis &gt; ZRANGE salary 0 -1 WITHSCORES             # 显示整个有序集成员</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;3500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;5000&quot;</span><br><span class="line">5) &quot;boss&quot;</span><br><span class="line">6) &quot;10086&quot;</span><br><span class="line"></span><br><span class="line">redis &gt; ZRANGE salary 1 2 WITHSCORES              # 显示有序集下标区间 1 至 2 的成员</span><br><span class="line">1) &quot;tom&quot;</span><br><span class="line">2) &quot;5000&quot;</span><br><span class="line">3) &quot;boss&quot;</span><br><span class="line">4) &quot;10086&quot;</span><br><span class="line"></span><br><span class="line">redis &gt; ZRANGE salary 0 200000 WITHSCORES         # 测试 end 下标超出最大下标时的情况</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;3500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;5000&quot;</span><br><span class="line">5) &quot;boss&quot;</span><br><span class="line">6) &quot;10086&quot;</span><br><span class="line"></span><br><span class="line">redis &gt; ZRANGE salary 200000 3000000 WITHSCORES   # 测试当给定区间不存在于有序集时的情况</span><br><span class="line">(empty list or set)</span><br></pre></td></tr></table></figure>

<h2 id="6-ZRANGEBYSCORE"><a href="#6-ZRANGEBYSCORE" class="headerlink" title="6.ZRANGEBYSCORE"></a>6.ZRANGEBYSCORE</h2><p><strong>ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]</strong></p>
<p>返回有序集 <code>key</code> 中，所有 <code>score</code> 值介于 <code>min</code> 和 <code>max</code> 之间(包括等于 <code>min</code> 或 <code>max</code> )的成员。有序集成员按 <code>score</code> 值递增(从小到大)次序排列。</p>
<p>具有相同 <code>score</code> 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的，不需要额外的计算)。</p>
<p>可选的 <code>LIMIT</code> 参数指定返回结果的数量及区间(就像SQL中的 <code>SELECT LIMIT offset, count</code> )，注意当 <code>offset</code> 很大时，定位 <code>offset</code> 的操作可能需要遍历整个有序集，此过程最坏复杂度为 O(N) 时间。</p>
<p>可选的 <code>WITHSCORES</code> 参数决定结果集是单单返回有序集的成员，还是将有序集成员及其 <code>score</code> 值一起返回。</p>
<p>该选项自 Redis 2.0 版本起可用。</p>
<p><strong>区间及无限</strong></p>
<p><code>min</code> 和 <code>max</code> 可以是 <code>-inf</code> 和 <code>+inf</code> ，这样一来，你就可以在不知道有序集的最低和最高 <code>score</code> 值的情况下，使用 ZRANGEBYSCORE 这类命令。</p>
<p>默认情况下，区间的取值使用闭区间 (小于等于或大于等于)，你也可以通过给参数前增加 <code>(</code> 符号来使用可选的开区间 (小于或大于)。</p>
<p>举个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ZRANGEBYSCORE zset (1 5</span><br></pre></td></tr></table></figure>

<p>返回所有符合条件 <code>1 &lt; score &lt;= 5</code> 的成员，而</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ZRANGEBYSCORE zset (5 (10</span><br></pre></td></tr></table></figure>

<p>则返回所有符合条件 <code>5 &lt; score &lt; 10</code> 的成员。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.5</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数， <code>M</code> 为被结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>指定区间内，带有 <code>score</code> 值(可选)的有序集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZADD salary 2500 jack                        # 测试数据</span><br><span class="line">(integer) 0</span><br><span class="line">redis&gt; ZADD salary 5000 tom</span><br><span class="line">(integer) 0</span><br><span class="line">redis&gt; ZADD salary 12000 peter</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGEBYSCORE salary -inf +inf               # 显示整个有序集</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;tom&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGEBYSCORE salary -inf +inf WITHSCORES    # 显示整个有序集及成员的 score 值</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;2500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;5000&quot;</span><br><span class="line">5) &quot;peter&quot;</span><br><span class="line">6) &quot;12000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGEBYSCORE salary -inf 5000 WITHSCORES    # 显示工资 &lt;&#x3D;5000 的所有成员</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;2500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGEBYSCORE salary (5000 400000            # 显示工资大于 5000 小于等于 400000 的成员</span><br><span class="line">1) &quot;peter&quot;</span><br></pre></td></tr></table></figure>

<h2 id="7-ZRANK"><a href="#7-ZRANK" class="headerlink" title="7.ZRANK"></a>7.ZRANK</h2><p><strong>ZRANK key member</strong></p>
<p>返回有序集 <code>key</code> 中成员 <code>member</code> 的排名。其中有序集成员按 <code>score</code> 值递增(从小到大)顺序排列。</p>
<p>排名以 <code>0</code> 为底，也就是说， <code>score</code> 值最小的成员排名为 <code>0</code> 。</p>
<p>使用 <em>ZREVRANK</em> 命令可以获得成员按 <code>score</code> 值递减(从大到小)排列的排名。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N))</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>如果 <code>member</code> 是有序集 <code>key</code> 的成员，返回 <code>member</code> 的排名。如果 <code>member</code> 不是有序集 <code>key</code> 的成员，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES        # 显示所有成员及其 score 值</span><br><span class="line">1) &quot;peter&quot;</span><br><span class="line">2) &quot;3500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;4000&quot;</span><br><span class="line">5) &quot;jack&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANK salary tom                     # 显示 tom 的薪水排名，第二</span><br><span class="line">(integer) 1</span><br></pre></td></tr></table></figure>

<h2 id="8-ZREM"><a href="#8-ZREM" class="headerlink" title="8.ZREM"></a>8.ZREM</h2><p><strong>ZREM key member [member …]</strong></p>
<p>移除有序集 <code>key</code> 中的一个或多个成员，不存在的成员将被忽略。</p>
<p>当 <code>key</code> 存在但不是有序集类型时，返回一个错误。</p>
<p>在 Redis 2.4 版本以前， ZREM 每次只能删除一个元素。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(M*log(N))， <code>N</code> 为有序集的基数， <code>M</code> 为被成功移除的成员的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被成功移除的成员的数量，不包括被忽略的成员。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"># 测试数据</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES</span><br><span class="line">1) &quot;bing.com&quot;</span><br><span class="line">2) &quot;8&quot;</span><br><span class="line">3) &quot;baidu.com&quot;</span><br><span class="line">4) &quot;9&quot;</span><br><span class="line">5) &quot;google.com&quot;</span><br><span class="line">6) &quot;10&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除单个元素</span><br><span class="line"></span><br><span class="line">redis&gt; ZREM page_rank google.com</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES</span><br><span class="line">1) &quot;bing.com&quot;</span><br><span class="line">2) &quot;8&quot;</span><br><span class="line">3) &quot;baidu.com&quot;</span><br><span class="line">4) &quot;9&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除多个元素</span><br><span class="line"></span><br><span class="line">redis&gt; ZREM page_rank baidu.com bing.com</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE page_rank 0 -1 WITHSCORES</span><br><span class="line">(empty list or set)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 移除不存在元素</span><br><span class="line"></span><br><span class="line">redis&gt; ZREM page_rank non-exists-element</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="9-ZREMRANGEBYRANK"><a href="#9-ZREMRANGEBYRANK" class="headerlink" title="9.ZREMRANGEBYRANK"></a>9.ZREMRANGEBYRANK</h2><p><strong>ZREMRANGEBYRANK key start stop</strong></p>
<p>移除有序集 <code>key</code> 中，指定排名(rank)区间内的所有成员。</p>
<p>区间分别以下标参数 <code>start</code> 和 <code>stop</code> 指出，包含 <code>start</code> 和 <code>stop</code> 在内。</p>
<p>下标参数 <code>start</code> 和 <code>stop</code> 都以 <code>0</code> 为底，也就是说，以 <code>0</code> 表示有序集第一个成员，以 <code>1</code> 表示有序集第二个成员，以此类推。</p>
<p>你也可以使用负数下标，以 <code>-1</code> 表示最后一个成员， <code>-2</code> 表示倒数第二个成员，以此类推。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数，而 <code>M</code> 为被移除成员的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被移除成员的数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZADD salary 2000 jack</span><br><span class="line">(integer) 1</span><br><span class="line">redis&gt; ZADD salary 5000 tom</span><br><span class="line">(integer) 1</span><br><span class="line">redis&gt; ZADD salary 3500 peter</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; ZREMRANGEBYRANK salary 0 1       # 移除下标 0 至 1 区间内的成员</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES    # 有序集只剩下一个成员</span><br><span class="line">1) &quot;tom&quot;</span><br><span class="line">2) &quot;5000&quot;</span><br></pre></td></tr></table></figure>

<h2 id="10-ZREMRANGEBYSCORE"><a href="#10-ZREMRANGEBYSCORE" class="headerlink" title="10.ZREMRANGEBYSCORE"></a>10.ZREMRANGEBYSCORE</h2><p><strong>ZREMRANGEBYSCORE key min max</strong></p>
<p>移除有序集 <code>key</code> 中，所有 <code>score</code> 值介于 <code>min</code> 和 <code>max</code> 之间(包括等于 <code>min</code> 或 <code>max</code> )的成员。</p>
<p>自版本2.1.6开始， <code>score</code> 值等于 <code>min</code> 或 <code>max</code> 的成员也可以不包括在内，详情请参见 <em>ZRANGEBYSCORE</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数，而 <code>M</code> 为被移除成员的数量。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>被移除成员的数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES          # 显示有序集内所有成员及其 score 值</span><br><span class="line">1) &quot;tom&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;jack&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZREMRANGEBYSCORE salary 1500 3500      # 移除所有薪水在 1500 到 3500 内的员工</span><br><span class="line">(integer) 2</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES          # 剩下的有序集成员</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;5000&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-ZREVRANGE"><a href="#11-ZREVRANGE" class="headerlink" title="11.ZREVRANGE"></a>11.ZREVRANGE</h2><p><strong>ZREVRANGE key start stop [WITHSCORES]</strong></p>
<p>返回有序集 <code>key</code> 中，指定区间内的成员。</p>
<p>其中成员的位置按 <code>score</code> 值递减(从大到小)来排列。</p>
<p>具有相同 <code>score</code> 值的成员按字典序的逆序(reverse lexicographical order)排列。</p>
<p>除了成员按 <code>score</code> 值递减的次序排列这一点外， ZREVRANGE 命令的其他方面和 <em>ZRANGE</em> 命令一样。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数，而 <code>M</code> 为结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>指定区间内，带有 <code>score</code> 值(可选)的有序集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES        # 递增排列</span><br><span class="line">1) &quot;peter&quot;</span><br><span class="line">2) &quot;3500&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;4000&quot;</span><br><span class="line">5) &quot;jack&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZREVRANGE salary 0 -1 WITHSCORES     # 递减排列</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;5000&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;4000&quot;</span><br><span class="line">5) &quot;peter&quot;</span><br><span class="line">6) &quot;3500&quot;</span><br></pre></td></tr></table></figure>

<h2 id="12-ZREVRANGEBYSCORE"><a href="#12-ZREVRANGEBYSCORE" class="headerlink" title="12.ZREVRANGEBYSCORE"></a>12.ZREVRANGEBYSCORE</h2><p><strong>ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]</strong></p>
<p>返回有序集 <code>key</code> 中， <code>score</code> 值介于 <code>max</code> 和 <code>min</code> 之间(默认包括等于 <code>max</code> 或 <code>min</code> )的所有的成员。有序集成员按 <code>score</code> 值递减(从大到小)的次序排列。</p>
<p>具有相同 <code>score</code> 值的成员按字典序的逆序(reverse lexicographical order )排列。</p>
<p>除了成员按 <code>score</code> 值递减的次序排列这一点外， ZREVRANGEBYSCORE 命令的其他方面和 <em>ZRANGEBYSCORE</em> 命令一样。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N)+M)， <code>N</code> 为有序集的基数， <code>M</code> 为结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>指定区间内，带有 <code>score</code> 值(可选)的有序集成员的列表。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">redis &gt; ZADD salary 10086 jack</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD salary 5000 tom</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD salary 7500 peter</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD salary 3500 joe</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZREVRANGEBYSCORE salary +inf -inf   # 逆序排列所有成员</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;peter&quot;</span><br><span class="line">3) &quot;tom&quot;</span><br><span class="line">4) &quot;joe&quot;</span><br><span class="line"></span><br><span class="line">redis &gt; ZREVRANGEBYSCORE salary 10000 2000  # 逆序排列薪水介于 10000 和 2000 之间的成员</span><br><span class="line">1) &quot;peter&quot;</span><br><span class="line">2) &quot;tom&quot;</span><br><span class="line">3) &quot;joe&quot;</span><br></pre></td></tr></table></figure>

<h2 id="13-ZREVRANK"><a href="#13-ZREVRANK" class="headerlink" title="13.ZREVRANK"></a>13.ZREVRANK</h2><p><strong>ZREVRANK key member</strong></p>
<p>返回有序集 <code>key</code> 中成员 <code>member</code> 的排名。其中有序集成员按 <code>score</code> 值递减(从大到小)排序。</p>
<p>排名以 <code>0</code> 为底，也就是说， <code>score</code> 值最大的成员排名为 <code>0</code> 。</p>
<p>使用 <em>ZRANK</em> 命令可以获得成员按 <code>score</code> 值递增(从小到大)排列的排名。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(log(N))</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>如果 <code>member</code> 是有序集 <code>key</code> 的成员，返回 <code>member</code> 的排名。如果 <code>member</code> 不是有序集 <code>key</code> 的成员，返回 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; ZRANGE salary 0 -1 WITHSCORES     # 测试数据</span><br><span class="line">1) &quot;jack&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;tom&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZREVRANK salary peter     # peter 的工资排第二</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; ZREVRANK salary tom       # tom 的工资最高</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="14-ZSCORE"><a href="#14-ZSCORE" class="headerlink" title="14.ZSCORE"></a>14.ZSCORE</h2><p><strong>ZSCORE key member</strong></p>
<p>返回有序集 <code>key</code> 中，成员 <code>member</code> 的 <code>score</code> 值。</p>
<p>如果 <code>member</code> 元素不是有序集 <code>key</code> 的成员，或 <code>key</code> 不存在，返回 <code>nil</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值:</strong></p>
<p><code>member</code> 成员的 <code>score</code> 值，以字符串形式表示。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES    # 测试数据</span><br><span class="line">1) &quot;tom&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;peter&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;jack&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZSCORE salary peter              # 注意返回值是字符串</span><br><span class="line">&quot;3500&quot;</span><br></pre></td></tr></table></figure>

<h2 id="15-ZUNIONSTORE"><a href="#15-ZUNIONSTORE" class="headerlink" title="15.ZUNIONSTORE"></a>15.ZUNIONSTORE</h2><p><strong>ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]</strong></p>
<p>计算给定的一个或多个有序集的并集，其中给定 <code>key</code> 的数量必须以 <code>numkeys</code> 参数指定，并将该并集(结果集)储存到 <code>destination</code> 。</p>
<p>默认情况下，结果集中某个成员的 <code>score</code> 值是所有给定集下该成员 <code>score</code> 值之 <em>和</em> 。</p>
<p><strong>WEIGHTS</strong></p>
<p>使用 <code>WEIGHTS</code> 选项，你可以为 <em>每个</em> 给定有序集 <em>分别</em> 指定一个乘法因子(multiplication factor)，每个给定有序集的所有成员的 <code>score</code> 值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。</p>
<p>如果没有指定 <code>WEIGHTS</code> 选项，乘法因子默认设置为 <code>1</code> 。</p>
<p><strong>AGGREGATE</strong></p>
<p>使用 <code>AGGREGATE</code> 选项，你可以指定并集的结果集的聚合方式。</p>
<p>默认使用的参数 <code>SUM</code> ，可以将所有集合中某个成员的 <code>score</code> 值之 <em>和</em> 作为结果集中该成员的 <code>score</code> 值；使用参数 <code>MIN</code> ，可以将所有集合中某个成员的 <em>最小</em> <code>score</code> 值作为结果集中该成员的 <code>score</code> 值；而参数 <code>MAX</code> 则是将所有集合中某个成员的 <em>最大</em> <code>score</code> 值作为结果集中该成员的 <code>score</code> 值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N)+O(M log(M))， <code>N</code> 为给定有序集基数的总和， <code>M</code> 为结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>保存到 <code>destination</code> 的结果集的基数。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ZRANGE programmer 0 -1 WITHSCORES</span><br><span class="line">1) &quot;peter&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;jack&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;tom&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE manager 0 -1 WITHSCORES</span><br><span class="line">1) &quot;herry&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;mary&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;bob&quot;</span><br><span class="line">6) &quot;4000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ZUNIONSTORE salary 2 programmer manager WEIGHTS 1 3   # 公司决定加薪。。。除了程序员。。。</span><br><span class="line">(integer) 6</span><br><span class="line"></span><br><span class="line">redis&gt; ZRANGE salary 0 -1 WITHSCORES</span><br><span class="line">1) &quot;peter&quot;</span><br><span class="line">2) &quot;2000&quot;</span><br><span class="line">3) &quot;jack&quot;</span><br><span class="line">4) &quot;3500&quot;</span><br><span class="line">5) &quot;tom&quot;</span><br><span class="line">6) &quot;5000&quot;</span><br><span class="line">7) &quot;herry&quot;</span><br><span class="line">8) &quot;6000&quot;</span><br><span class="line">9) &quot;mary&quot;</span><br><span class="line">10) &quot;10500&quot;</span><br><span class="line">11) &quot;bob&quot;</span><br><span class="line">12) &quot;12000&quot;</span><br></pre></td></tr></table></figure>

<h2 id="16-ZINTERSTORE"><a href="#16-ZINTERSTORE" class="headerlink" title="16.ZINTERSTORE"></a>16.ZINTERSTORE</h2><p><strong>ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]</strong></p>
<p>计算给定的一个或多个有序集的交集，其中给定 <code>key</code> 的数量必须以 <code>numkeys</code> 参数指定，并将该交集(结果集)储存到 <code>destination</code> 。</p>
<p>默认情况下，结果集中某个成员的 <code>score</code> 值是所有给定集下该成员 <code>score</code> 值之和.</p>
<p>关于 <code>WEIGHTS</code> 和 <code>AGGREGATE</code> 选项的描述，参见 <em>ZUNIONSTORE</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度:</strong></p>
<p>O(N<em>K)+O(M</em>log(M))， <code>N</code> 为给定 <code>key</code> 中基数最小的有序集， <code>K</code> 为给定有序集的数量， <code>M</code> 为结果集的基数。</p>
</li>
<li><p><strong>返回值:</strong></p>
<p>保存到 <code>destination</code> 的结果集的基数。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">redis &gt; ZADD mid_test 70 &quot;Li Lei&quot;</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD mid_test 70 &quot;Han Meimei&quot;</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD mid_test 99.5 &quot;Tom&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZADD fin_test 88 &quot;Li Lei&quot;</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD fin_test 75 &quot;Han Meimei&quot;</span><br><span class="line">(integer) 1</span><br><span class="line">redis &gt; ZADD fin_test 99.5 &quot;Tom&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis &gt; ZINTERSTORE sum_point 2 mid_test fin_test</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line">redis &gt; ZRANGE sum_point 0 -1 WITHSCORES     # 显示有序集内所有成员及其 score 值</span><br><span class="line">1) &quot;Han Meimei&quot;</span><br><span class="line">2) &quot;145&quot;</span><br><span class="line">3) &quot;Li Lei&quot;</span><br><span class="line">4) &quot;158&quot;</span><br><span class="line">5) &quot;Tom&quot;</span><br><span class="line">6) &quot;199&quot;</span><br></pre></td></tr></table></figure>

<h2 id="17-ZSCAN"><a href="#17-ZSCAN" class="headerlink" title="17.ZSCAN"></a>17.ZSCAN</h2><p><strong>ZSCAN key cursor [MATCH pattern] [COUNT count]</strong></p>
<p>详细信息请参考 [<em>SCAN</em> 命令。</p>
<h1 id="七、Pub-Sub（发布-订阅）"><a href="#七、Pub-Sub（发布-订阅）" class="headerlink" title="七、Pub/Sub（发布/订阅）"></a>七、Pub/Sub（发布/订阅）</h1><h2 id="1-PSUBSCRIBE"><a href="#1-PSUBSCRIBE" class="headerlink" title="1.PSUBSCRIBE"></a>1.PSUBSCRIBE</h2><p><strong>PSUBSCRIBE pattern [pattern …]</strong></p>
<p>订阅一个或多个符合给定模式的频道。</p>
<p>每个模式以 <code>*</code> 作为匹配符，比如 <code>it*</code> 匹配所有以 <code>it</code> 开头的频道( <code>it.news</code> 、 <code>it.blog</code> 、 <code>it.tweets</code> 等等)， <code>news.*</code> 匹配所有以 <code>news.</code> 开头的频道( <code>news.it</code> 、 <code>news.global.today</code> 等等)，诸如此类。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 是订阅的模式的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>接收到的信息(请参见下面的代码说明)。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"># 订阅 news.* 和 tweet.* 两个模式</span><br><span class="line"></span><br><span class="line"># 第 1 - 6 行是执行 psubscribe 之后的反馈信息</span><br><span class="line"># 第 7 - 10 才是接收到的第一条信息</span><br><span class="line"># 第 11 - 14 是第二条</span><br><span class="line"># 以此类推。。。</span><br><span class="line"></span><br><span class="line">redis&gt; psubscribe news.* tweet.*</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;psubscribe&quot;                  # 返回值的类型：显示订阅成功</span><br><span class="line">2) &quot;news.*&quot;                      # 订阅的模式</span><br><span class="line">3) (integer) 1                   # 目前已订阅的模式的数量</span><br><span class="line"></span><br><span class="line">1) &quot;psubscribe&quot;</span><br><span class="line">2) &quot;tweet.*&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line">1) &quot;pmessage&quot;                    # 返回值的类型：信息</span><br><span class="line">2) &quot;news.*&quot;                      # 信息匹配的模式</span><br><span class="line">3) &quot;news.it&quot;                     # 信息本身的目标频道</span><br><span class="line">4) &quot;Google buy Motorola&quot;         # 信息的内容</span><br><span class="line"></span><br><span class="line">1) &quot;pmessage&quot;</span><br><span class="line">2) &quot;tweet.*&quot;</span><br><span class="line">3) &quot;tweet.huangz&quot;</span><br><span class="line">4) &quot;hello&quot;</span><br><span class="line"></span><br><span class="line">1) &quot;pmessage&quot;</span><br><span class="line">2) &quot;tweet.*&quot;</span><br><span class="line">3) &quot;tweet.joe&quot;</span><br><span class="line">4) &quot;@huangz morning&quot;</span><br><span class="line"></span><br><span class="line">1) &quot;pmessage&quot;</span><br><span class="line">2) &quot;news.*&quot;</span><br><span class="line">3) &quot;news.life&quot;</span><br><span class="line">4) &quot;An apple a day, keep doctors away&quot;</span><br></pre></td></tr></table></figure>

<h2 id="2-PUBLISH"><a href="#2-PUBLISH" class="headerlink" title="2.PUBLISH"></a>2.PUBLISH</h2><p><strong>PUBLISH channel message</strong></p>
<p>将信息 <code>message</code> 发送到指定的频道 <code>channel</code> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N+M)，其中 <code>N</code> 是频道 <code>channel</code> 的订阅者数量，而 <code>M</code> 则是使用模式订阅(subscribed patterns)的客户端的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>接收到信息 <code>message</code> 的订阅者数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 对没有订阅者的频道发送信息</span><br><span class="line"></span><br><span class="line">redis&gt; publish bad_channel &quot;can any body hear me?&quot;</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 向有一个订阅者的频道发送信息</span><br><span class="line"></span><br><span class="line">redis&gt; publish msg &quot;good morning&quot;</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 向有多个订阅者的频道发送信息</span><br><span class="line"></span><br><span class="line">redis&gt; publish chat_room &quot;hello~ everyone&quot;</span><br><span class="line">(integer) 3</span><br></pre></td></tr></table></figure>

<h2 id="3-PUBSUB"><a href="#3-PUBSUB" class="headerlink" title="3.PUBSUB"></a>3.PUBSUB</h2><p><strong>PUBSUB <subcommand> [argument [argument …]]</strong></p>
<p><em>PUBSUB</em> 是一个查看订阅与发布系统状态的内省命令， 它由数个不同格式的子命令组成， 以下将分别对这些子命令进行介绍。</p>
<p><strong>可用版本：</strong> &gt;= 2.8.0</p>
<h3 id="PUBSUB-CHANNELS-pattern"><a href="#PUBSUB-CHANNELS-pattern" class="headerlink" title="PUBSUB CHANNELS [pattern]"></a>PUBSUB CHANNELS [pattern]</h3><p>列出当前的活跃频道。</p>
<p>活跃频道指的是那些至少有一个订阅者的频道， 订阅模式的客户端不计算在内。</p>
<p><code>pattern</code> 参数是可选的：</p>
<ul>
<li>如果不给出 <code>pattern</code> 参数，那么列出订阅与发布系统中的所有活跃频道。</li>
<li>如果给出 <code>pattern</code> 参数，那么只列出和给定模式 <code>pattern</code> 相匹配的那些活跃频道。</li>
</ul>
<p><strong>复杂度：</strong> O(N) ， <code>N</code> 为活跃频道的数量（对于长度较短的频道和模式来说，将进行模式匹配的复杂度视为常数）。</p>
<p><strong>返回值：</strong> 一个由活跃频道组成的列表。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"># client-1 订阅 news.it 和 news.sport 两个频道</span><br><span class="line"></span><br><span class="line">client-1&gt; SUBSCRIBE news.it news.sport</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.it&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.sport&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line"># client-2 订阅 news.it 和 news.internet 两个频道</span><br><span class="line"></span><br><span class="line">client-2&gt; SUBSCRIBE news.it news.internet</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.it&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.internet&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line"># 首先， client-3 打印所有活跃频道</span><br><span class="line"># 注意，即使一个频道有多个订阅者，它也只输出一次，比如 news.it</span><br><span class="line"></span><br><span class="line">client-3&gt; PUBSUB CHANNELS</span><br><span class="line">1) &quot;news.sport&quot;</span><br><span class="line">2) &quot;news.internet&quot;</span><br><span class="line">3) &quot;news.it&quot;</span><br><span class="line"></span><br><span class="line"># 接下来， client-3 打印那些与模式 news.i* 相匹配的活跃频道</span><br><span class="line"># 因为 news.sport 不匹配 news.i* ，所以它没有被打印</span><br><span class="line"></span><br><span class="line">redis&gt; PUBSUB CHANNELS news.i*</span><br><span class="line">1) &quot;news.internet&quot;</span><br><span class="line">2) &quot;news.it&quot;</span><br></pre></td></tr></table></figure>

<h3 id="PUBSUB-NUMSUB-channel-1-…-channel-N"><a href="#PUBSUB-NUMSUB-channel-1-…-channel-N" class="headerlink" title="PUBSUB NUMSUB [channel-1 … channel-N]"></a>PUBSUB NUMSUB [channel-1 … channel-N]</h3><p>返回给定频道的订阅者数量， 订阅模式的客户端不计算在内。</p>
<p><strong>复杂度：</strong> O(N) ， <code>N</code> 为给定频道的数量。</p>
<p><strong>返回值：</strong> 一个多条批量回复（Multi-bulk reply），回复中包含给定的频道，以及频道的订阅者数量。 格式为：频道 <code>channel-1</code> ， <code>channel-1</code> 的订阅者数量，频道 <code>channel-2</code> ， <code>channel-2</code> 的订阅者数量，诸如此类。 回复中频道的排列顺序和执行命令时给定频道的排列顺序一致。 不给定任何频道而直接调用这个命令也是可以的， 在这种情况下， 命令只返回一个空列表。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"># client-1 订阅 news.it 和 news.sport 两个频道</span><br><span class="line"></span><br><span class="line">client-1&gt; SUBSCRIBE news.it news.sport</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.it&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.sport&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line"># client-2 订阅 news.it 和 news.internet 两个频道</span><br><span class="line"></span><br><span class="line">client-2&gt; SUBSCRIBE news.it news.internet</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.it&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;news.internet&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line"># client-3 打印各个频道的订阅者数量</span><br><span class="line"></span><br><span class="line">client-3&gt; PUBSUB NUMSUB news.it news.internet news.sport news.music</span><br><span class="line">1) &quot;news.it&quot;    # 频道</span><br><span class="line">2) &quot;2&quot;          # 订阅该频道的客户端数量</span><br><span class="line">3) &quot;news.internet&quot;</span><br><span class="line">4) &quot;1&quot;</span><br><span class="line">5) &quot;news.sport&quot;</span><br><span class="line">6) &quot;1&quot;</span><br><span class="line">7) &quot;news.music&quot; # 没有任何订阅者</span><br><span class="line">8) &quot;0&quot;</span><br></pre></td></tr></table></figure>

<h3 id="PUBSUB-NUMPAT"><a href="#PUBSUB-NUMPAT" class="headerlink" title="PUBSUB NUMPAT"></a>PUBSUB NUMPAT</h3><p>返回订阅模式的数量。</p>
<p>注意， 这个命令返回的不是订阅模式的客户端的数量， 而是客户端订阅的所有模式的数量总和。</p>
<p><strong>复杂度：</strong> O(1) 。</p>
<p><strong>返回值：</strong> 一个整数回复（Integer reply）。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"># client-1 订阅 news.* 和 discount.* 两个模式</span><br><span class="line"></span><br><span class="line">client-1&gt; PSUBSCRIBE news.* discount.*</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;psubscribe&quot;</span><br><span class="line">2) &quot;news.*&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line">1) &quot;psubscribe&quot;</span><br><span class="line">2) &quot;discount.*&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line"># client-2 订阅 tweet.* 一个模式</span><br><span class="line"></span><br><span class="line">client-2&gt; PSUBSCRIBE tweet.*</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;psubscribe&quot;</span><br><span class="line">2) &quot;tweet.*&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line"></span><br><span class="line"># client-3 返回当前订阅模式的数量为 3</span><br><span class="line"></span><br><span class="line">client-3&gt; PUBSUB NUMPAT</span><br><span class="line">(integer) 3</span><br><span class="line"></span><br><span class="line"># 注意，当有多个客户端订阅相同的模式时，相同的订阅也被计算在 PUBSUB NUMPAT 之内</span><br><span class="line"># 比如说，再新建一个客户端 client-4 ，让它也订阅 news.* 频道</span><br><span class="line"></span><br><span class="line">client-4&gt; PSUBSCRIBE news.*</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;psubscribe&quot;</span><br><span class="line">2) &quot;news.*&quot;</span><br><span class="line">3) (integer) 1</span><br><span class="line"></span><br><span class="line"># 这时再计算被订阅模式的数量，就会得到数量为 4</span><br><span class="line"></span><br><span class="line">client-3&gt; PUBSUB NUMPAT</span><br><span class="line">(integer) 4</span><br></pre></td></tr></table></figure>

<h2 id="4-PUNSUBSCRIBE"><a href="#4-PUNSUBSCRIBE" class="headerlink" title="4.PUNSUBSCRIBE"></a>4.PUNSUBSCRIBE</h2><p><strong>PUNSUBSCRIBE [pattern [pattern …]]</strong></p>
<p>指示客户端退订所有给定模式。</p>
<p>如果没有模式被指定，也即是，一个无参数的 <code>PUNSUBSCRIBE</code> 调用被执行，那么客户端使用 <em>PSUBSCRIBE</em> 命令订阅的所有模式都会被退订。在这种情况下，命令会返回一个信息，告知客户端所有被退订的模式。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N+M) ，其中 <code>N</code> 是客户端已订阅的模式的数量， <code>M</code> 则是系统中所有客户端订阅的模式的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>这个命令在不同的客户端中有不同的表现。</p>
</li>
</ul>
<h2 id="5-SUBSCRIBE"><a href="#5-SUBSCRIBE" class="headerlink" title="5.SUBSCRIBE"></a>5.SUBSCRIBE</h2><p><strong>SUBSCRIBE channel [channel …]</strong></p>
<p>订阅给定的一个或多个频道的信息。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)，其中 <code>N</code> 是订阅的频道的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>接收到的信息(请参见下面的代码说明)。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"># 订阅 msg 和 chat_room 两个频道</span><br><span class="line"></span><br><span class="line"># 1 - 6 行是执行 subscribe 之后的反馈信息</span><br><span class="line"># 第 7 - 9 行才是接收到的第一条信息</span><br><span class="line"># 第 10 - 12 行是第二条</span><br><span class="line"></span><br><span class="line">redis&gt; subscribe msg chat_room</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) &quot;subscribe&quot;       # 返回值的类型：显示订阅成功</span><br><span class="line">2) &quot;msg&quot;             # 订阅的频道名字</span><br><span class="line">3) (integer) 1       # 目前已订阅的频道数量</span><br><span class="line"></span><br><span class="line">1) &quot;subscribe&quot;</span><br><span class="line">2) &quot;chat_room&quot;</span><br><span class="line">3) (integer) 2</span><br><span class="line"></span><br><span class="line">1) &quot;message&quot;         # 返回值的类型：信息</span><br><span class="line">2) &quot;msg&quot;             # 来源(从那个频道发送过来)</span><br><span class="line">3) &quot;hello moto&quot;      # 信息内容</span><br><span class="line"></span><br><span class="line">1) &quot;message&quot;</span><br><span class="line">2) &quot;chat_room&quot;</span><br><span class="line">3) &quot;testing...haha&quot;</span><br></pre></td></tr></table></figure>

<h2 id="6-UNSUBSCRIBE"><a href="#6-UNSUBSCRIBE" class="headerlink" title="6.UNSUBSCRIBE"></a>6.UNSUBSCRIBE</h2><p><strong>UNSUBSCRIBE [channel [channel …]]</strong></p>
<p>指示客户端退订给定的频道。</p>
<p>如果没有频道被指定，也即是，一个无参数的 <code>UNSUBSCRIBE</code> 调用被执行，那么客户端使用 <em>SUBSCRIBE</em> 命令订阅的所有频道都会被退订。在这种情况下，命令会返回一个信息，告知客户端所有被退订的频道。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N) ， <code>N</code> 是客户端已订阅的频道的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>这个命令在不同的客户端中有不同的表现。</p>
</li>
</ul>
<h1 id="八、Transaction（事务）"><a href="#八、Transaction（事务）" class="headerlink" title="八、Transaction（事务）"></a>八、Transaction（事务）</h1><h2 id="1-DISCARD"><a href="#1-DISCARD" class="headerlink" title="1.DISCARD"></a>1.DISCARD</h2><p><strong>DISCARD</strong></p>
<p>取消事务，放弃执行事务块内的所有命令。</p>
<p>如果正在使用 <em>WATCH</em> 命令监视某个(或某些) key，那么取消所有监视，等同于执行命令 <em>UNWATCH</em> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PING</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; SET greeting &quot;hello&quot;</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; DISCARD</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h2 id="2-EXEC"><a href="#2-EXEC" class="headerlink" title="2.EXEC"></a>2.EXEC</h2><p><strong>EXEC</strong></p>
<p>执行所有事务块内的命令。</p>
<p>假如某个(或某些) key 正处于 <em>WATCH</em> 命令的监视之下，且事务块中有和这个(或这些) key 相关的命令，那么 EXEC 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效，否则该事务被打断(abort)。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>事务块内所有命令的时间复杂度的总和。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>事务块内所有命令的返回值，按命令执行的先后顺序排列。当操作被打断时，返回空值 <code>nil</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"># 事务被成功执行</span><br><span class="line"></span><br><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; PING</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC</span><br><span class="line">1) (integer) 1</span><br><span class="line">2) (integer) 2</span><br><span class="line">3) (integer) 3</span><br><span class="line">4) PONG</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 监视 key ，且事务成功执行</span><br><span class="line"></span><br><span class="line">redis&gt; WATCH lock lock_times</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET lock &quot;huangz&quot;</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR lock_times</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC</span><br><span class="line">1) OK</span><br><span class="line">2) (integer) 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 监视 key ，且事务被打断</span><br><span class="line"></span><br><span class="line">redis&gt; WATCH lock lock_times</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; MULTI</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SET lock &quot;joe&quot;        # 就在这时，另一个客户端修改了 lock_times 的值</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR lock_times</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC                  # 因为 lock_times 被修改， joe 的事务执行失败</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="3-MULTI"><a href="#3-MULTI" class="headerlink" title="3.MULTI"></a>3.MULTI</h2><p><strong>MULTI</strong></p>
<p>标记一个事务块的开始。</p>
<p>事务块内的多条命令会按照先后顺序被放进一个队列当中，最后由 <em>EXEC</em> 命令原子性(atomic)地执行。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; MULTI            # 标记事务开始</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id     # 多条命令按顺序入队</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; INCR user_id</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; PING</span><br><span class="line">QUEUED</span><br><span class="line"></span><br><span class="line">redis&gt; EXEC             # 执行</span><br><span class="line">1) (integer) 1</span><br><span class="line">2) (integer) 2</span><br><span class="line">3) (integer) 3</span><br><span class="line">4) PONG</span><br></pre></td></tr></table></figure>

<h2 id="4-UNWATCH"><a href="#4-UNWATCH" class="headerlink" title="4.UNWATCH"></a>4.UNWATCH</h2><p><strong>UNWATCH</strong></p>
<p>取消 <em>WATCH</em> 命令对所有 key 的监视。</p>
<p>如果在执行 <em>WATCH</em> 命令之后， <em>EXEC</em> 命令或 <em>DISCARD</em> 命令先被执行了的话，那么就不需要再执行 UNWATCH 了。</p>
<p>因为 <em>EXEC</em> 命令会执行事务，因此 <em>WATCH</em> 命令的效果已经产生了；而 <em>DISCARD</em> 命令在取消事务的同时也会取消所有对 key 的监视，因此这两个命令执行之后，就没有必要执行 UNWATCH 了。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; WATCH lock lock_times</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; UNWATCH</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h2 id="5-WATCH"><a href="#5-WATCH" class="headerlink" title="5.WATCH"></a>5.WATCH</h2><p><strong>WATCH key [key …]</strong></p>
<p>监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; WATCH lock lock_times</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h1 id="九、Script（脚本）"><a href="#九、Script（脚本）" class="headerlink" title="九、Script（脚本）"></a>九、Script（脚本）</h1><h2 id="1-EVAL"><a href="#1-EVAL" class="headerlink" title="1.EVAL"></a>1.EVAL</h2><p><strong>EVAL script numkeys key [key …] arg [arg …]</strong></p>
<p>从 Redis 2.6.0 版本开始，通过内置的 Lua 解释器，可以使用 [EVAL 命令对 Lua 脚本进行求值。</p>
<p><code>script</code> 参数是一段 Lua 5.1 脚本程序，它会被运行在 Redis 服务器上下文中，这段脚本不必(也不应该)定义为一个 Lua 函数。</p>
<p><code>numkeys</code> 参数用于指定键名参数的个数。</p>
<p>键名参数 <code>key [key ...]</code> 从 [EVAL 的第三个参数开始算起，表示在脚本中所用到的那些 Redis 键(key)，这些键名参数可以在 Lua 中通过全局变量 <code>KEYS</code> 数组，用 <code>1</code> 为基址的形式访问( <code>KEYS[1]</code> ， <code>KEYS[2]</code> ，以此类推)。</p>
<p>在命令的最后，那些不是键名参数的附加参数 <code>arg [arg ...]</code> ，可以在 Lua 中通过全局变量 <code>ARGV</code> 数组访问，访问的形式和 <code>KEYS</code> 变量类似( <code>ARGV[1]</code> 、 <code>ARGV[2]</code> ，诸如此类)。</p>
<p>上面这几段长长的说明可以用一个简单的例子来概括：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt; eval &quot;return &#123;KEYS[1],KEYS[2],ARGV[1],ARGV[2]&#125;&quot; 2 key1 key2 first second</span><br><span class="line">1) &quot;key1&quot;</span><br><span class="line">2) &quot;key2&quot;</span><br><span class="line">3) &quot;first&quot;</span><br><span class="line">4) &quot;second&quot;</span><br></pre></td></tr></table></figure>

<p>其中 <code>&quot;return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}&quot;</code> 是被求值的 Lua 脚本，数字 <code>2</code> 指定了键名参数的数量， <code>key1</code> 和 <code>key2</code> 是键名参数，分别使用 <code>KEYS[1]</code> 和 <code>KEYS[2]</code> 访问，而最后的 <code>first</code> 和 <code>second</code> 则是附加参数，可以通过 <code>ARGV[1]</code> 和 <code>ARGV[2]</code> 访问它们。</p>
<p>在 Lua 脚本中，可以使用两个不同函数来执行 Redis 命令，它们分别是：</p>
<ul>
<li><code>redis.call()</code></li>
<li><code>redis.pcall()</code></li>
</ul>
<p>这两个函数的唯一区别在于它们使用不同的方式处理执行命令所产生的错误，在后面的『错误处理』部分会讲到这一点。</p>
<p><code>redis.call()</code> 和 <code>redis.pcall()</code> 两个函数的参数可以是任何格式良好(well formed)的 Redis 命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; eval &quot;return redis.call(&#39;set&#39;,&#39;foo&#39;,&#39;bar&#39;)&quot; 0</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>需要注意的是，上面这段脚本的确实现了将键 <code>foo</code> 的值设为 <code>bar</code> 的目的，但是，它违反了 EVAL 命令的语义，因为脚本里使用的所有键都应该由 <code>KEYS</code> 数组来传递，就像这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; eval &quot;return redis.call(&#39;set&#39;,KEYS[1],&#39;bar&#39;)&quot; 1 foo</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>要求使用正确的形式来传递键(key)是有原因的，因为不仅仅是 [EVAL 这个命令，所有的 Redis 命令，在执行之前都会被分析，籍此来确定命令会对哪些键进行操作。</p>
<p>因此，对于 EVAL 命令来说，必须使用正确的形式来传递键，才能确保分析工作正确地执行。除此之外，使用正确的形式来传递键还有很多其他好处，它的一个特别重要的用途就是确保 Redis 集群可以将你的请求发送到正确的集群节点。(对 Redis 集群的工作还在进行当中，但是脚本功能被设计成可以与集群功能保持兼容。)不过，这条规矩并不是强制性的，从而使得用户有机会滥用(abuse) Redis 单实例配置(single instance configuration)，代价是这样写出的脚本不能被 Redis 集群所兼容。</p>
<h3 id="在-Lua-数据类型和-Redis-数据类型之间转换"><a href="#在-Lua-数据类型和-Redis-数据类型之间转换" class="headerlink" title="在 Lua 数据类型和 Redis 数据类型之间转换"></a>在 Lua 数据类型和 Redis 数据类型之间转换</h3><p>当 Lua 通过 <code>call()</code> 或 <code>pcall()</code> 函数执行 Redis 命令的时候，命令的返回值会被转换成 Lua 数据结构。同样地，当 Lua 脚本在 Redis 内置的解释器里运行时，Lua 脚本的返回值也会被转换成 Redis 协议(protocol)，然后由 [EVAL 将值返回给客户端。</p>
<p>数据类型之间的转换遵循这样一个设计原则：如果将一个 Redis 值转换成 Lua 值，之后再将转换所得的 Lua 值转换回 Redis 值，那么这个转换所得的 Redis 值应该和最初时的 Redis 值一样。</p>
<p>换句话说， Lua 类型和 Redis 类型之间存在着一一对应的转换关系。</p>
<p>以下列出的是详细的转换规则：</p>
<p>从 Redis 转换到 Lua ：</p>
<ul>
<li>Redis integer reply -&gt; Lua number / Redis 整数转换成 Lua 数字</li>
<li>Redis bulk reply -&gt; Lua string / Redis bulk 回复转换成 Lua 字符串</li>
<li>Redis multi bulk reply -&gt; Lua table (may have other Redis data types nested) / Redis 多条 bulk 回复转换成 Lua 表，表内可能有其他别的 Redis 数据类型</li>
<li>Redis status reply -&gt; Lua table with a single ok field containing the status / Redis 状态回复转换成 Lua 表，表内的 <code>ok</code> 域包含了状态信息</li>
<li>Redis error reply -&gt; Lua table with a single err field containing the error / Redis 错误回复转换成 Lua 表，表内的 <code>err</code> 域包含了错误信息</li>
<li>Redis Nil bulk reply and Nil multi bulk reply -&gt; Lua false boolean type / Redis 的 Nil 回复和 Nil 多条回复转换成 Lua 的布尔值 <code>false</code></li>
</ul>
<p>从 Lua 转换到 Redis：</p>
<ul>
<li>Lua number -&gt; Redis integer reply / Lua 数字转换成 Redis 整数</li>
<li>Lua string -&gt; Redis bulk reply / Lua 字符串转换成 Redis bulk 回复</li>
<li>Lua table (array) -&gt; Redis multi bulk reply / Lua 表(数组)转换成 Redis 多条 bulk 回复</li>
<li>Lua table with a single ok field -&gt; Redis status reply / 一个带单个 <code>ok</code> 域的 Lua 表，转换成 Redis 状态回复</li>
<li>Lua table with a single err field -&gt; Redis error reply / 一个带单个 <code>err</code> 域的 Lua 表，转换成 Redis 错误回复</li>
<li>Lua boolean false -&gt; Redis Nil bulk reply / Lua 的布尔值 <code>false</code> 转换成 Redis 的 Nil bulk 回复</li>
</ul>
<p>从 Lua 转换到 Redis 有一条额外的规则，这条规则没有和它对应的从 Redis 转换到 Lua 的规则：</p>
<ul>
<li>Lua boolean true -&gt; Redis integer reply with value of 1 / Lua 布尔值 <code>true</code> 转换成 Redis 整数回复中的 <code>1</code></li>
</ul>
<p>以下是几个类型转换的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&gt; eval &quot;return 10&quot; 0</span><br><span class="line">(integer) 10</span><br><span class="line"></span><br><span class="line">&gt; eval &quot;return &#123;1,2,&#123;3,&#39;Hello World!&#39;&#125;&#125;&quot; 0</span><br><span class="line">1) (integer) 1</span><br><span class="line">2) (integer) 2</span><br><span class="line">3) 1) (integer) 3</span><br><span class="line">   2) &quot;Hello World!&quot;</span><br><span class="line"></span><br><span class="line">&gt; eval &quot;return redis.call(&#39;get&#39;,&#39;foo&#39;)&quot; 0</span><br><span class="line">&quot;bar&quot;</span><br></pre></td></tr></table></figure>

<p>在上面的三个代码示例里，前两个演示了如何将 Lua 值转换成 Redis 值，最后一个例子更复杂一些，它演示了一个将 Redis 值转换成 Lua 值，然后再将 Lua 值转换成 Redis 值的类型转过程。</p>
<h3 id="脚本的原子性"><a href="#脚本的原子性" class="headerlink" title="脚本的原子性"></a>脚本的原子性</h3><p>Redis 使用单个 Lua 解释器去运行所有脚本，并且， Redis 也保证脚本会以原子性(atomic)的方式执行：当某个脚本正在运行的时候，不会有其他脚本或 Redis 命令被执行。这和使用 <em>MULTI</em> / <em>EXEC</em> 包围的事务很类似。在其他别的客户端看来，脚本的效果(effect)要么是不可见的(not visible)，要么就是已完成的(already completed)。</p>
<p>另一方面，这也意味着，执行一个运行缓慢的脚本并不是一个好主意。写一个跑得很快很顺溜的脚本并不难，因为脚本的运行开销(overhead)非常少，但是当你不得不使用一些跑得比较慢的脚本时，请小心，因为当这些蜗牛脚本在慢吞吞地运行的时候，其他客户端会因为服务器正忙而无法执行命令。</p>
<h3 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h3><p>前面的命令介绍部分说过， <code>redis.call()</code> 和 <code>redis.pcall()</code> 的唯一区别在于它们对错误处理的不同。</p>
<p>当 <code>redis.call()</code> 在执行命令的过程中发生错误时，脚本会停止执行，并返回一个脚本错误，错误的输出信息会说明错误造成的原因：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; lpush foo a</span><br><span class="line">(integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; eval &quot;return redis.call(&#39;get&#39;, &#39;foo&#39;)&quot; 0</span><br><span class="line">(error) ERR Error running script (call to f_282297a0228f48cd3fc6a55de6316f31422f5d17): ERR Operation against a key holding the wrong kind of value</span><br></pre></td></tr></table></figure>

<p>和 <code>redis.call()</code> 不同， <code>redis.pcall()</code> 出错时并不引发(raise)错误，而是返回一个带 <code>err</code> 域的 Lua 表(table)，用于表示错误：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; EVAL &quot;return redis.pcall(&#39;get&#39;, &#39;foo&#39;)&quot; 0</span><br><span class="line">(error) ERR Operation against a key holding the wrong kind of value</span><br></pre></td></tr></table></figure>

<h3 id="带宽和-EVALSHA"><a href="#带宽和-EVALSHA" class="headerlink" title="带宽和 EVALSHA"></a>带宽和 EVALSHA</h3><p>[EVAL 命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)。Redis 有一个内部的缓存机制，因此它不会每次都重新编译脚本，不过在很多场合，付出无谓的带宽来传送脚本主体并不是最佳选择。</p>
<p>为了减少带宽的消耗， Redis 实现了 EVALSHA 命令，它的作用和 [EVAL 一样，都用于对脚本求值，但它接受的第一个参数不是脚本，而是脚本的 SHA1 校验和(sum)。</p>
<p>EVALSHA 命令的表现如下：</p>
<ul>
<li>如果服务器还记得给定的 SHA1 校验和所指定的脚本，那么执行这个脚本</li>
<li>如果服务器不记得给定的 SHA1 校验和所指定的脚本，那么它返回一个特殊的错误，提醒用户使用 [EVAL 代替 EVALSHA</li>
</ul>
<p>以下是示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&gt; set foo bar</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">&gt; eval &quot;return redis.call(&#39;get&#39;,&#39;foo&#39;)&quot; 0</span><br><span class="line">&quot;bar&quot;</span><br><span class="line"></span><br><span class="line">&gt; evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0</span><br><span class="line">&quot;bar&quot;</span><br><span class="line"></span><br><span class="line">&gt; evalsha ffffffffffffffffffffffffffffffffffffffff 0</span><br><span class="line">(error) &#96;NOSCRIPT&#96; No matching script. Please use [EVAL](&#x2F;commands&#x2F;eval).</span><br></pre></td></tr></table></figure>

<p>客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ，并期望着要使用的脚本已经保存在服务器上了，只有当 <code>NOSCRIPT</code> 错误发生时，才使用 EVAL 命令重新发送脚本，这样就可以最大限度地节省带宽。</p>
<p>这也说明了执行 EVAL 命令时，使用正确的格式来传递键名参数和附加参数的重要性：因为如果将参数硬写在脚本中，那么每次当参数改变的时候，都要重新发送脚本，即使脚本的主体并没有改变，相反，通过使用正确的格式来传递键名参数和附加参数，就可以在脚本主体不变的情况下，直接使用 EVALSHA 命令对脚本进行复用，免去了无谓的带宽消耗。</p>
<h3 id="脚本缓存"><a href="#脚本缓存" class="headerlink" title="脚本缓存"></a>脚本缓存</h3><p>Redis 保证所有被运行过的脚本都会被永久保存在脚本缓存当中，这意味着，当 EVAL 命令在一个 Redis 实例上成功执行某个脚本之后，随后针对这个脚本的所有 EVALSHA 命令都会成功执行。</p>
<p>刷新脚本缓存的唯一办法是显式地调用 <code>SCRIPT FLUSH</code> 命令，这个命令会清空运行过的所有脚本的缓存。通常只有在云计算环境中，Redis 实例被改作其他客户或者别的应用程序的实例时，才会执行这个命令。</p>
<p>缓存可以长时间储存而不产生内存问题的原因是，它们的体积非常小，而且数量也非常少，即使脚本在概念上类似于实现一个新命令，即使在一个大规模的程序里有成百上千的脚本，即使这些脚本会经常修改，即便如此，储存这些脚本的内存仍然是微不足道的。</p>
<p>事实上，用户会发现 Redis 不移除缓存中的脚本实际上是一个好主意。比如说，对于一个和 Redis 保持持久化链接(persistent connection)的程序来说，它可以确信，执行过一次的脚本会一直保留在内存当中，因此它可以在流水线中使用 EVALSHA 命令而不必担心因为找不到所需的脚本而产生错误(稍候我们会看到在流水线中执行脚本的相关问题)。</p>
<h3 id="SCRIPT-命令"><a href="#SCRIPT-命令" class="headerlink" title="SCRIPT 命令"></a>SCRIPT 命令</h3><p>Redis 提供了以下几个 SCRIPT 命令，用于对脚本子系统(scripting subsystem)进行控制：</p>
<ul>
<li>*SCRIPT FLUSH ：清除所有脚本缓存</li>
<li><em>SCRIPT EXISTS</em> ：根据给定的脚本校验和，检查指定的脚本是否存在于脚本缓存</li>
<li><em>SCRIPT LOAD</em> ：将一个脚本装入脚本缓存，但并不立即运行它</li>
<li><em>SCRIPT KILL</em> ：杀死当前正在运行的脚本</li>
</ul>
<h3 id="纯函数脚本"><a href="#纯函数脚本" class="headerlink" title="纯函数脚本"></a>纯函数脚本</h3><p>在编写脚本方面，一个重要的要求就是，脚本应该被写成纯函数(pure function)。</p>
<p>也就是说，脚本应该具有以下属性：</p>
<ul>
<li>对于同样的数据集输入，给定相同的参数，脚本执行的 Redis 写命令总是相同的。脚本执行的操作不能依赖于任何隐藏(非显式)数据，不能依赖于脚本在执行过程中、或脚本在不同执行时期之间可能变更的状态，并且它也不能依赖于任何来自 I/O 设备的外部输入。</li>
</ul>
<p>使用系统时间(system time)，调用像 <em>RANDOMKEY</em> 那样的随机命令，或者使用 Lua 的随机数生成器，类似以上的这些操作，都会造成脚本的求值无法每次都得出同样的结果。</p>
<p>为了确保脚本符合上面所说的属性， Redis 做了以下工作：</p>
<ul>
<li>Lua 没有访问系统时间或者其他内部状态的命令</li>
<li>Redis 会返回一个错误，阻止这样的脚本运行： 这些脚本在执行随机命令之后(比如 <em>RANDOMKEY</em> 、 <em>SRANDMEMBER</em> 或 <em>TIME</em> 等)，还会执行可以修改数据集的 Redis 命令。如果脚本只是执行只读操作，那么就没有这一限制。注意，随机命令并不一定就指那些带 RAND 字眼的命令，任何带有非确定性的命令都会被认为是随机命令，比如 <em>TIME</em> 命令就是这方面的一个很好的例子。</li>
<li>每当从 Lua 脚本中调用那些返回无序元素的命令时，执行命令所得的数据在返回给 Lua 之前会先执行一个静默(slient)的字典序排序(lexicographical sorting)。举个例子，因为 Redis 的 Set 保存的是无序的元素，所以在 Redis 命令行客户端中直接执行 <em>SMEMBERS</em> ，返回的元素是无序的，但是，假如在脚本中执行 <code>redis.call(&quot;smembers&quot;, KEYS[1])</code> ，那么返回的总是排过序的元素。</li>
<li>对 Lua 的伪随机数生成函数 <code>math.random</code> 和 <code>math.randomseed</code> 进行修改，使得每次在运行新脚本的时候，总是拥有同样的 seed 值。这意味着，每次运行脚本时，只要不使用 <code>math.randomseed</code> ，那么 <code>math.random</code> 产生的随机数序列总是相同的。</li>
</ul>
<p>尽管有那么多的限制，但用户还是可以用一个简单的技巧写出带随机行为的脚本(如果他们需要的话)。</p>
<p>假设现在我们要编写一个 Redis 脚本，这个脚本从列表中弹出 N 个随机数。一个 Ruby 写的例子如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">require &#39;rubygems&#39;</span><br><span class="line">require &#39;redis&#39;</span><br><span class="line"></span><br><span class="line">r &#x3D; Redis.new</span><br><span class="line"></span><br><span class="line">RandomPushScript &#x3D; &lt;&lt;EOF</span><br><span class="line">    local i &#x3D; tonumber(ARGV[1])</span><br><span class="line">    local res</span><br><span class="line">    while (i &gt; 0) do</span><br><span class="line">        res &#x3D; redis.call(&#39;lpush&#39;,KEYS[1],math.random())</span><br><span class="line">        i &#x3D; i-1</span><br><span class="line">    end</span><br><span class="line">    return res</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">r.del(:mylist)</span><br><span class="line">puts r.eval(RandomPushScript,[:mylist],[10,rand(2**32)])</span><br></pre></td></tr></table></figure>

<p>这个程序每次运行都会生成带有以下元素的列表：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&gt; lrange mylist 0 -1</span><br><span class="line">1) &quot;0.74509509873814&quot;</span><br><span class="line">2) &quot;0.87390407681181&quot;</span><br><span class="line">3) &quot;0.36876626981831&quot;</span><br><span class="line">4) &quot;0.6921941534114&quot;</span><br><span class="line">5) &quot;0.7857992587545&quot;</span><br><span class="line">6) &quot;0.57730350670279&quot;</span><br><span class="line">7) &quot;0.87046522734243&quot;</span><br><span class="line">8) &quot;0.09637165539729&quot;</span><br><span class="line">9) &quot;0.74990198051087&quot;</span><br><span class="line">10) &quot;0.17082803611217&quot;</span><br></pre></td></tr></table></figure>

<p>上面的 Ruby 程序每次都只生成同样的列表，用途并不是太大。那么，该怎样修改这个脚本，使得它仍然是一个纯函数(符合 Redis 的要求)，但是每次调用都可以产生不同的随机元素呢？</p>
<p>一个简单的办法是，为脚本添加一个额外的参数，让这个参数作为 Lua 的随机数生成器的 seed 值，这样的话，只要给脚本传入不同的 seed ，脚本就会生成不同的列表元素。</p>
<p>以下是修改后的脚本：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">RandomPushScript &#x3D; &lt;&lt;EOF</span><br><span class="line">    local i &#x3D; tonumber(ARGV[1])</span><br><span class="line">    local res</span><br><span class="line">    math.randomseed(tonumber(ARGV[2]))</span><br><span class="line">    while (i &gt; 0) do</span><br><span class="line">        res &#x3D; redis.call(&#39;lpush&#39;,KEYS[1],math.random())</span><br><span class="line">        i &#x3D; i-1</span><br><span class="line">    end</span><br><span class="line">    return res</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">r.del(:mylist)</span><br><span class="line">puts r.eval(RandomPushScript,1,:mylist,10,rand(2**32))</span><br></pre></td></tr></table></figure>

<p>尽管对于同样的 seed ，上面的脚本产生的列表元素是一样的(因为它是一个纯函数)，但是只要每次在执行脚本的时候传入不同的 seed ，我们就可以得到带有不同随机元素的列表。</p>
<p>Seed 会在复制(replication link)和写 AOF 文件时作为一个参数来传播，保证在载入 AOF 文件或附属节点(slave)处理脚本时， seed 仍然可以及时得到更新。</p>
<p>注意，Redis 实现保证 <code>math.random</code> 和 <code>math.randomseed</code> 的输出和运行 Redis 的系统架构无关，无论是 32 位还是 64 位系统，无论是小端(little endian)还是大端(big endian)系统，这两个函数的输出总是相同的。</p>
<h3 id="全局变量保护"><a href="#全局变量保护" class="headerlink" title="全局变量保护"></a>全局变量保护</h3><p>为了防止不必要的数据泄漏进 Lua 环境， Redis 脚本不允许创建全局变量。如果一个脚本需要在多次执行之间维持某种状态，它应该使用 Redis key 来进行状态保存。</p>
<p>企图在脚本中访问一个全局变量(不论这个变量是否存在)将引起脚本停止， EVAL 命令会返回一个错误：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:6379&gt; eval &#39;a&#x3D;10&#39; 0</span><br><span class="line">(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable &#39;a&#39;</span><br></pre></td></tr></table></figure>

<p>Lua 的 debug 工具，或者其他设施，比如打印（alter）用于实现全局保护的 meta table ，都可以用于实现全局变量保护。</p>
<p>实现全局变量保护并不难，不过有时候还是会不小心而为之。一旦用户在脚本中混入了 Lua 全局状态，那么 AOF 持久化和复制（replication）都会无法保证，所以，请不要使用全局变量。</p>
<p>避免引入全局变量的一个诀窍是：将脚本中用到的所有变量都使用 <code>local</code> 关键字定义为局部变量。</p>
<h3 id="库"><a href="#库" class="headerlink" title="库"></a>库</h3><p>Redis 内置的 Lua 解释器加载了以下 Lua 库：</p>
<ul>
<li><code>base</code></li>
<li><code>table</code></li>
<li><code>string</code></li>
<li><code>math</code></li>
<li><code>debug</code></li>
<li><code>cjson</code></li>
<li><code>cmsgpack</code></li>
</ul>
<p>其中 <code>cjson</code> 库可以让 Lua 以非常快的速度处理 JSON 数据，除此之外，其他别的都是 Lua 的标准库。</p>
<p>每个 Redis 实例都保证会加载上面列举的库，从而确保每个 Redis 脚本的运行环境都是相同的。</p>
<h3 id="使用脚本散发-Redis-日志"><a href="#使用脚本散发-Redis-日志" class="headerlink" title="使用脚本散发 Redis 日志"></a>使用脚本散发 Redis 日志</h3><p>在 Lua 脚本中，可以通过调用 <code>redis.log</code> 函数来写 Redis 日志(log)：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis.log(loglevel, message)</span><br></pre></td></tr></table></figure>

<p>其中， <code>message</code> 参数是一个字符串，而 <code>loglevel</code> 参数可以是以下任意一个值：</p>
<ul>
<li><code>redis.LOG_DEBUG</code></li>
<li><code>redis.LOG_VERBOSE</code></li>
<li><code>redis.LOG_NOTICE</code></li>
<li><code>redis.LOG_WARNING</code></li>
</ul>
<p>上面的这些等级(level)和标准 Redis 日志的等级相对应。</p>
<p>对于脚本散发(emit)的日志，只有那些和当前 Redis 实例所设置的日志等级相同或更高级的日志才会被散发。</p>
<p>以下是一个日志示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis.log(redis.LOG_WARNING, &quot;Something is wrong with this script.&quot;)</span><br></pre></td></tr></table></figure>

<p>执行上面的函数会产生这样的信息：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[32343] 22 Mar 15:21:39 # Something is wrong with this script.</span><br></pre></td></tr></table></figure>

<h2 id="沙箱-sandbox-和最大执行时间"><a href="#沙箱-sandbox-和最大执行时间" class="headerlink" title="沙箱(sandbox)和最大执行时间"></a>沙箱(sandbox)和最大执行时间</h2><p>脚本应该仅仅用于传递参数和对 Redis 数据进行处理，它不应该尝试去访问外部系统(比如文件系统)，或者执行任何系统调用。</p>
<p>除此之外，脚本还有一个最大执行时间限制，它的默认值是 5 秒钟，一般正常运作的脚本通常可以在几分之几毫秒之内完成，花不了那么多时间，这个限制主要是为了防止因编程错误而造成的无限循环而设置的。</p>
<p>最大执行时间的长短由 <code>lua-time-limit</code> 选项来控制(以毫秒为单位)，可以通过编辑 <code>redis.conf</code> 文件或者使用 <em>CONFIG GET</em> 和 <em>CONFIG SET</em> 命令来修改它。</p>
<p>当一个脚本达到最大执行时间的时候，它并不会自动被 Redis 结束，因为 Redis 必须保证脚本执行的原子性，而中途停止脚本的运行意味着可能会留下未处理完的数据在数据集(data set)里面。</p>
<p>因此，当脚本运行的时间超过最大执行时间后，以下动作会被执行：</p>
<ul>
<li>Redis 记录一个脚本正在超时运行</li>
<li>Redis 开始重新接受其他客户端的命令请求，但是只有 <code>SCRIPT KILL</code> 和 <code>SHUTDOWN NOSAVE</code> 两个命令会被处理，对于其他命令请求， Redis 服务器只是简单地返回 <code>BUSY</code> 错误。</li>
<li>可以使用 <code>SCRIPT KILL</code> 命令将一个仅执行只读命令的脚本杀死，因为只读命令并不修改数据，因此杀死这个脚本并不破坏数据的完整性</li>
<li>如果脚本已经执行过写命令，那么唯一允许执行的操作就是 <code>SHUTDOWN NOSAVE</code> ，它通过停止服务器来阻止当前数据集写入磁盘</li>
</ul>
<h3 id="流水线-pipeline-上下文-context-中的-EVALSHA"><a href="#流水线-pipeline-上下文-context-中的-EVALSHA" class="headerlink" title="流水线(pipeline)上下文(context)中的 EVALSHA"></a>流水线(pipeline)上下文(context)中的 EVALSHA</h3><p>在流水线请求的上下文中使用 EVALSHA 命令时，要特别小心，因为在流水线中，必须保证命令的执行顺序。</p>
<p>一旦在流水线中因为 EVALSHA 命令而发生 NOSCRIPT 错误，那么这个流水线就再也没有办法重新执行了，否则的话，命令的执行顺序就会被打乱。</p>
<p>为了防止出现以上所说的问题，客户端库实现应该实施以下的其中一项措施：</p>
<ul>
<li><p>总是在流水线中使用 EVAL 命令</p>
</li>
<li><p>检查流水线中要用到的所有命令，找到其中的 [EVAL 命令，并使用 <em>SCRIPT EXISTS</em> 命令检查要用到的脚本是不是全都已经保存在缓存里面了。如果所需的全部脚本都可以在缓存里找到，那么就可以放心地将所有 EVAL 命令改成 EVALSHA 命令，否则的话，就要在流水线的顶端(top)将缺少的脚本用 <em>SCRIPT LOAD</em> 命令加上去。</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>EVAL 和 EVALSHA 可以在 O(1) 复杂度内找到要被执行的脚本，其余的复杂度取决于执行的脚本本身。</p>
</li>
</ul>
<h2 id="2-EVALSHA"><a href="#2-EVALSHA" class="headerlink" title="2.EVALSHA"></a>2.EVALSHA</h2><p><strong>EVALSHA sha1 numkeys key [key …] arg [arg …]</strong></p>
<p>根据给定的 sha1 校验码，对缓存在服务器中的脚本进行求值。</p>
<p>将脚本缓存到服务器的操作可以通过 <em>SCRIPT LOAD</em> 命令进行。</p>
<p>这个命令的其他地方，比如参数的传入方式，都和 <em>EVAL</em> 命令一样。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>根据脚本的复杂度而定。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SCRIPT LOAD &quot;return &#39;hello moto&#39;&quot;</span><br><span class="line">&quot;232fd51614574cf0867b83d384a5e898cfd24e5a&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; EVALSHA &quot;232fd51614574cf0867b83d384a5e898cfd24e5a&quot; 0</span><br><span class="line">&quot;hello moto&quot;</span><br></pre></td></tr></table></figure>

<h2 id="3-SCRIPT-EXISTS"><a href="#3-SCRIPT-EXISTS" class="headerlink" title="3.SCRIPT EXISTS"></a>3.SCRIPT EXISTS</h2><p><strong>SCRIPT EXISTS script [script …]</strong></p>
<p>给定一个或多个脚本的 SHA1 校验和，返回一个包含 <code>0</code> 和 <code>1</code> 的列表，表示校验和所指定的脚本是否已经被保存在缓存当中。</p>
<p>关于使用 Redis 对 Lua 脚本进行求值的更多信息，请参见 <em>EVAL</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N) , <code>N</code> 为给定的 SHA1 校验和的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个列表，包含 <code>0</code> 和 <code>1</code> ，前者表示脚本不存在于缓存，后者表示脚本已经在缓存里面了。列表中的元素和给定的 SHA1 校验和保持对应关系，比如列表的第三个元素的值就表示第三个 SHA1 校验和所指定的脚本在缓存中的状态。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SCRIPT LOAD &quot;return &#39;hello moto&#39;&quot;    # 载入一个脚本</span><br><span class="line">&quot;232fd51614574cf0867b83d384a5e898cfd24e5a&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a</span><br><span class="line">1) (integer) 1</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT FLUSH     # 清空缓存</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a</span><br><span class="line">1) (integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="4-SCRIPT-FLUSH"><a href="#4-SCRIPT-FLUSH" class="headerlink" title="4.SCRIPT FLUSH"></a>4.SCRIPT FLUSH</h2><p><strong>SCRIPT FLUSH</strong></p>
<p>清除所有 Lua 脚本缓存。</p>
<p>关于使用 Redis 对 Lua 脚本进行求值的更多信息，请参见 <em>EVAL</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>复杂度：</strong></p>
<p>O(N) ， <code>N</code> 为缓存中脚本的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code></p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SCRIPT FLUSH</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h2 id="5-SCRIPT-KILL"><a href="#5-SCRIPT-KILL" class="headerlink" title="5.SCRIPT KILL"></a>5.SCRIPT KILL</h2><p><strong>SCRIPT KILL</strong></p>
<p>杀死当前正在运行的 Lua 脚本，当且仅当这个脚本没有执行过任何写操作时，这个命令才生效。</p>
<p>这个命令主要用于终止运行时间过长的脚本，比如一个因为 BUG 而发生无限 loop 的脚本，诸如此类。</p>
<p>SCRIPT KILL 执行之后，当前正在运行的脚本会被杀死，执行这个脚本的客户端会从 <em>EVAL</em> 命令的阻塞当中退出，并收到一个错误作为返回值。</p>
<p>另一方面，假如当前正在运行的脚本已经执行过写操作，那么即使执行 SCRIPT KILL ，也无法将它杀死，因为这是违反 Lua 脚本的原子性执行原则的。在这种情况下，唯一可行的办法是使用 <code>SHUTDOWN NOSAVE</code> 命令，通过停止整个 Redis 进程来停止脚本的运行，并防止不完整(half-written)的信息被写入数据库中。</p>
<p>关于使用 Redis 对 Lua 脚本进行求值的更多信息，请参见 <em>EVAL</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行成功返回 <code>OK</code> ，否则返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 没有脚本在执行时</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT KILL</span><br><span class="line">(error) ERR No scripts in execution right now.</span><br><span class="line"></span><br><span class="line"># 成功杀死脚本时</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT KILL</span><br><span class="line">OK</span><br><span class="line">(1.30s)</span><br><span class="line"></span><br><span class="line"># 尝试杀死一个已经执行过写操作的脚本，失败</span><br><span class="line"></span><br><span class="line">redis&gt; SCRIPT KILL</span><br><span class="line">(error) ERR Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command.</span><br><span class="line">(1.69s)</span><br></pre></td></tr></table></figure>

<p>以下是脚本被杀死之后，返回给执行脚本的客户端的错误：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; EVAL &quot;while true do end&quot; 0</span><br><span class="line">(error) ERR Error running script (call to f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): Script killed by user with SCRIPT KILL...</span><br><span class="line">(5.00s)</span><br></pre></td></tr></table></figure>

<h2 id="6-SCRIPT-LOAD"><a href="#6-SCRIPT-LOAD" class="headerlink" title="6.SCRIPT LOAD"></a>6.SCRIPT LOAD</h2><p><strong>SCRIPT LOAD script</strong></p>
<p>将脚本 <code>script</code> 添加到脚本缓存中，但并不立即执行这个脚本。</p>
<p><em>EVAL</em> 命令也会将脚本添加到脚本缓存中，但是它会立即对输入的脚本进行求值。</p>
<p>如果给定的脚本已经在缓存里面了，那么不做动作。</p>
<p>在脚本被加入到缓存之后，通过 EVALSHA 命令，可以使用脚本的 SHA1 校验和来调用这个脚本。</p>
<p>脚本可以在缓存中保留无限长的时间，直到执行 *SCRIPT FLUSH 为止。</p>
<p>关于使用 Redis 对 Lua 脚本进行求值的更多信息，请参见 <em>EVAL</em> 命令。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N) , <code>N</code> 为脚本的长度(以字节为单位)。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>给定 <code>script</code> 的 SHA1 校验和</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SCRIPT LOAD &quot;return &#39;hello moto&#39;&quot;</span><br><span class="line">&quot;232fd51614574cf0867b83d384a5e898cfd24e5a&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0</span><br><span class="line">&quot;hello moto&quot;</span><br></pre></td></tr></table></figure>

<h1 id="十、Connection（连接）"><a href="#十、Connection（连接）" class="headerlink" title="十、Connection（连接）"></a>十、Connection（连接）</h1><h2 id="1-AUTH"><a href="#1-AUTH" class="headerlink" title="1.AUTH"></a>1.AUTH</h2><p><strong>AUTH password</strong></p>
<p>通过设置配置文件中 <code>requirepass</code> 项的值(使用命令 <code>CONFIG SET requirepass password</code> )，可以使用密码来保护 Redis 服务器。</p>
<p>如果开启了密码保护的话，在每次连接 Redis 服务器之后，就要使用 <code>AUTH</code> 命令解锁，解锁之后才能使用其他 Redis 命令。</p>
<p>如果 <code>AUTH</code> 命令给定的密码 <code>password</code> 和配置文件中的密码相符的话，服务器会返回 <code>OK</code> 并开始接受命令输入。</p>
<p>另一方面，假如密码不匹配的话，服务器将返回一个错误，并要求客户端需重新输入密码。</p>
<p>因为 Redis 高性能的特点，在很短时间内尝试猜测非常多个密码是有可能的，因此请确保使用的密码足够复杂和足够长，以免遭受密码猜测攻击。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>密码匹配时返回 <code>OK</code> ，否则返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"># 设置密码</span><br><span class="line"></span><br><span class="line">redis&gt; CONFIG SET requirepass secret_password   # 将密码设置为 secret_password</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; QUIT                                     # 退出再连接，让新密码对客户端生效</span><br><span class="line"></span><br><span class="line">[huangz@mypad]$ redis</span><br><span class="line"></span><br><span class="line">redis&gt; PING                                     # 未验证密码，操作被拒绝</span><br><span class="line">(error) ERR operation not permitted</span><br><span class="line"></span><br><span class="line">redis&gt; AUTH wrong_password_testing              # 尝试输入错误的密码</span><br><span class="line">(error) ERR invalid password</span><br><span class="line"></span><br><span class="line">redis&gt; AUTH secret_password                     # 输入正确的密码</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; PING                                     # 密码验证成功，可以正常操作命令了</span><br><span class="line">PONG</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 清空密码</span><br><span class="line"></span><br><span class="line">redis&gt; CONFIG SET requirepass &quot;&quot;   # 通过将密码设为空字符来清空密码</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; QUIT</span><br><span class="line"></span><br><span class="line">$ redis                            # 重新进入客户端</span><br><span class="line"></span><br><span class="line">redis&gt; PING                        # 执行命令不再需要密码，清空密码操作成功</span><br><span class="line">PONG</span><br></pre></td></tr></table></figure>

<h2 id="2-ECHO"><a href="#2-ECHO" class="headerlink" title="2.ECHO"></a>2.ECHO</h2><p><strong>ECHO message</strong></p>
<p>打印一个特定的信息 <code>message</code> ，测试时使用。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p><code>message</code> 自身。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; ECHO &quot;Hello Moto&quot;</span><br><span class="line">&quot;Hello Moto&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; ECHO &quot;Goodbye Moto&quot;</span><br><span class="line">&quot;Goodbye Moto&quot;</span><br></pre></td></tr></table></figure>

<h2 id="3-PING"><a href="#3-PING" class="headerlink" title="3.PING"></a>3.PING</h2><p><strong>PING</strong></p>
<p>使用客户端向 Redis 服务器发送一个 <code>PING</code> ，如果服务器运作正常的话，会返回一个 <code>PONG</code> 。</p>
<p>通常用于测试与服务器的连接是否仍然生效，或者用于测量延迟值。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>如果连接正常就返回一个 <code>PONG</code> ，否则返回一个连接错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># 客户端和服务器连接正常</span><br><span class="line"></span><br><span class="line">redis&gt; PING</span><br><span class="line">PONG</span><br><span class="line"></span><br><span class="line"># 客户端和服务器连接不正常(网络不正常或服务器未能正常运行)</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; PING</span><br><span class="line">Could not connect to Redis at 127.0.0.1:6379: Connection refused</span><br></pre></td></tr></table></figure>

<h2 id="4-QUIT"><a href="#4-QUIT" class="headerlink" title="4.QUIT"></a>4.QUIT</h2><p><strong>QUIT</strong></p>
<p>请求服务器关闭与当前客户端的连接。</p>
<p>一旦所有等待中的回复(如果有的话)顺利写入到客户端，连接就会被关闭。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> (但是不会被打印显示，因为当时 Redis-cli 已经退出)。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ redis</span><br><span class="line"></span><br><span class="line">redis&gt; QUIT</span><br><span class="line"></span><br><span class="line">$</span><br></pre></td></tr></table></figure>

<h2 id="5-SELECT"><a href="#5-SELECT" class="headerlink" title="5.SELECT"></a>5.SELECT</h2><p><strong>SELECT index</strong></p>
<p>切换到指定的数据库，数据库索引号 <code>index</code> 用数字值指定，以 <code>0</code> 作为起始索引值。</p>
<p>默认使用 <code>0</code> 号数据库。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p><code>OK</code></p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SET db_number 0         # 默认使用 0 号数据库</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 1                # 使用 1 号数据库</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis[1]&gt; GET db_number        # 已经切换到 1 号数据库，注意 Redis 现在的命令提示符多了个 [1]</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line">redis[1]&gt; SET db_number 1</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis[1]&gt; GET db_number</span><br><span class="line">&quot;1&quot;</span><br><span class="line"></span><br><span class="line">redis[1]&gt; SELECT 3             # 再切换到 3 号数据库</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis[3]&gt;                      # 提示符从 [1] 改变成了 [3]</span><br></pre></td></tr></table></figure>

<h1 id="十一、Server（服务器）"><a href="#十一、Server（服务器）" class="headerlink" title="十一、Server（服务器）"></a>十一、Server（服务器）</h1><h2 id="1-BGREWRITEAOF"><a href="#1-BGREWRITEAOF" class="headerlink" title="1.BGREWRITEAOF"></a>1.BGREWRITEAOF</h2><p><strong>BGREWRITEAOF</strong></p>
<p>执行一个 AOF文件 重写操作。重写会创建一个当前 AOF 文件的体积优化版本。</p>
<p>即使 BGREWRITEAOF 执行失败，也不会有任何数据丢失，因为旧的 AOF 文件在 BGREWRITEAOF 成功之前不会被修改。</p>
<p>重写操作只会在没有其他持久化工作在后台执行时被触发，也就是说：</p>
<ul>
<li>如果 Redis 的子进程正在执行快照的保存工作，那么 AOF 重写的操作会被预定(scheduled)，等到保存工作完成之后再执行 AOF 重写。在这种情况下， BGREWRITEAOF 的返回值仍然是 <code>OK</code> ，但还会加上一条额外的信息，说明 BGREWRITEAOF 要等到保存操作完成之后才能执行。在 Redis 2.6 或以上的版本，可以使用 <em>INFO</em> 命令查看 BGREWRITEAOF 是否被预定。</li>
<li>如果已经有别的 AOF 文件重写在执行，那么 BGREWRITEAOF 返回一个错误，并且这个新的 BGREWRITEAOF 请求也不会被预定到下次执行。</li>
</ul>
<p>从 Redis 2.4 开始， AOF 重写由 Redis 自行触发， BGREWRITEAOF 仅仅用于手动触发重写操作。</p>
<p>请移步 持久化文档(英文) 查看更多相关细节。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要追加到 AOF 文件中的数据数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>反馈信息。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; BGREWRITEAOF</span><br><span class="line">Background append only file rewriting started</span><br></pre></td></tr></table></figure>

<h2 id="2-BGSAVE"><a href="#2-BGSAVE" class="headerlink" title="2.BGSAVE"></a>2.BGSAVE</h2><p>在后台异步(Asynchronously)保存当前数据库的数据到磁盘。</p>
<p>BGSAVE 命令执行之后立即返回 <code>OK</code> ，然后 Redis fork 出一个新子进程，原来的 Redis 进程(父进程)继续处理客户端请求，而子进程则负责将数据保存到磁盘，然后退出。</p>
<p>客户端可以通过 <em>LASTSAVE</em> 命令查看相关信息，判断 BGSAVE 命令是否执行成功。</p>
<p>请移步 持久化文档 查看更多相关细节。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要保存到数据库中的 key 的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>反馈信息。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; BGSAVE</span><br><span class="line">Background saving started</span><br></pre></td></tr></table></figure>

<h2 id="3-CLIENT-GETNAME"><a href="#3-CLIENT-GETNAME" class="headerlink" title="3.CLIENT GETNAME"></a>3.CLIENT GETNAME</h2><p><strong>CLIENT GETNAME</strong></p>
<p>返回 <em>CLIENT SETNAME</em> 命令为连接设置的名字。</p>
<p>因为新创建的连接默认是没有名字的， 对于没有名字的连接， <em>CLIENT GETNAME</em> 返回空白回复。</p>
<ul>
<li><p><strong>可用版本</strong></p>
<blockquote>
<p>= 2.6.9</p>
</blockquote>
</li>
<li><p><strong>时间复杂度</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值</strong></p>
<p>如果连接没有设置名字，那么返回空白回复；如果有设置名字，那么返回名字。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># 新连接默认没有名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT GETNAME</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line"># 设置名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT SETNAME hello-world-connection</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># 返回名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT GETNAME</span><br><span class="line">&quot;hello-world-connection&quot;</span><br></pre></td></tr></table></figure>

<h2 id="4-CLIENT-KILL"><a href="#4-CLIENT-KILL" class="headerlink" title="4.CLIENT KILL"></a>4.CLIENT KILL</h2><p><strong>CLIENT KILL ip:port</strong></p>
<p>关闭地址为 <code>ip:port</code> 的客户端。</p>
<p><code>ip:port</code> 应该和 <em>CLIENT LIST</em> 命令输出的其中一行匹配。</p>
<p>因为 Redis 使用单线程设计，所以当 Redis 正在执行命令的时候，不会有客户端被断开连接。</p>
<p>如果要被断开连接的客户端正在执行命令，那么当这个命令执行之后，在发送下一个命令的时候，它就会收到一个网络错误，告知它自身的连接已被关闭。</p>
<ul>
<li><p><strong>可用版本</strong></p>
<blockquote>
<p>= 2.4.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度</strong></p>
<p>O(N) ， N 为已连接的客户端数量。</p>
</li>
<li><p><strong>返回值</strong></p>
<p>当指定的客户端存在，且被成功关闭时，返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"># 列出所有已连接客户端</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT LIST</span><br><span class="line">addr&#x3D;127.0.0.1:43501 fd&#x3D;5 age&#x3D;10 idle&#x3D;0 flags&#x3D;N db&#x3D;0 sub&#x3D;0 psub&#x3D;0 multi&#x3D;-1 qbuf&#x3D;0 qbuf-free&#x3D;32768 obl&#x3D;0 oll&#x3D;0 omem&#x3D;0 events&#x3D;r cmd&#x3D;client</span><br><span class="line"></span><br><span class="line"># 杀死当前客户端的连接</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT KILL 127.0.0.1:43501</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># 之前的连接已经被关闭，CLI 客户端又重新建立了连接</span><br><span class="line"># 之前的端口是 43501 ，现在是 43504</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT LIST</span><br><span class="line">addr&#x3D;127.0.0.1:43504 fd&#x3D;5 age&#x3D;0 idle&#x3D;0 flags&#x3D;N db&#x3D;0 sub&#x3D;0 psub&#x3D;0 multi&#x3D;-1 qbuf&#x3D;0 qbuf-free&#x3D;32768 obl&#x3D;0 oll&#x3D;0 omem&#x3D;0 events&#x3D;r cmd&#x3D;client</span><br></pre></td></tr></table></figure>

<h2 id="5-CLIENT-LIST"><a href="#5-CLIENT-LIST" class="headerlink" title="5.CLIENT LIST"></a>5.CLIENT LIST</h2><p><strong>CLIENT LIST</strong></p>
<p>以人类可读的格式，返回所有连接到服务器的客户端信息和统计数据。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CLIENT LIST</span><br><span class="line">addr&#x3D;127.0.0.1:43143 fd&#x3D;6 age&#x3D;183 idle&#x3D;0 flags&#x3D;N db&#x3D;0 sub&#x3D;0 psub&#x3D;0 multi&#x3D;-1 qbuf&#x3D;0 qbuf-free&#x3D;32768 obl&#x3D;0 oll&#x3D;0 omem&#x3D;0 events&#x3D;r cmd&#x3D;client</span><br><span class="line">addr&#x3D;127.0.0.1:43163 fd&#x3D;5 age&#x3D;35 idle&#x3D;15 flags&#x3D;N db&#x3D;0 sub&#x3D;0 psub&#x3D;0 multi&#x3D;-1 qbuf&#x3D;0 qbuf-free&#x3D;0 obl&#x3D;0 oll&#x3D;0 omem&#x3D;0 events&#x3D;r cmd&#x3D;ping</span><br><span class="line">addr&#x3D;127.0.0.1:43167 fd&#x3D;7 age&#x3D;24 idle&#x3D;6 flags&#x3D;N db&#x3D;0 sub&#x3D;0 psub&#x3D;0 multi&#x3D;-1 qbuf&#x3D;0 qbuf-free&#x3D;0 obl&#x3D;0 oll&#x3D;0 omem&#x3D;0 events&#x3D;r cmd&#x3D;get</span><br></pre></td></tr></table></figure>

<ul>
<li><p><strong>可用版本</strong></p>
<blockquote>
<p>= 2.4.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度</strong></p>
<p>O(N) ， N 为连接到服务器的客户端数量。</p>
</li>
<li><p><strong>返回值</strong></p>
<p>命令返回多行字符串，这些字符串按以下形式被格式化：每个已连接客户端对应一行（以 <code>LF</code> 分割）每行字符串由一系列 <code>属性=值</code> 形式的域组成，每个域之间以空格分开以下是域的含义：<code>addr</code> ： 客户端的地址和端口<code>fd</code> ： 套接字所使用的文件描述符<code>age</code> ： 以秒计算的已连接时长<code>idle</code> ： 以秒计算的空闲时长<code>flags</code> ： 客户端 flag （见下文）<code>db</code> ： 该客户端正在使用的数据库 ID<code>sub</code> ： 已订阅频道的数量<code>psub</code> ： 已订阅模式的数量<code>multi</code> ： 在事务中被执行的命令数量<code>qbuf</code> ： 查询缓存的长度（ <code>0</code> 表示没有查询在等待）<code>qbuf-free</code> ： 查询缓存的剩余空间（ <code>0</code> 表示没有剩余空间）<code>obl</code> ： 输出缓存的长度<code>oll</code> ： 输出列表的长度（当输出缓存没有剩余空间时，回复被入队到这个队列里）<code>omem</code> ： 输出缓存的内存占用量<code>events</code> ： 文件描述符事件（见下文）<code>cmd</code> ： 最近一次执行的命令客户端 flag 可以由以下部分组成：<code>O</code> ： 客户端是 MONITOR 模式下的附属节点（slave）<code>S</code> ： 客户端是一般模式下（normal）的附属节点<code>M</code> ： 客户端是主节点（master）<code>x</code> ： 客户端正在执行事务<code>b</code> ： 客户端正在等待阻塞事件<code>i</code> ： 客户端正在等待 VM I/O 操作（已废弃）<code>d</code> ： 一个受监视（watched）的键已被修改， <code>EXEC</code> 命令将失败<code>c</code> : 在将回复完整地写出之后，关闭链接<code>u</code> : 客户端未被阻塞（unblocked）<code>A</code> : 尽可能快地关闭连接<code>N</code> : 未设置任何 flag文件描述符事件可以是：<code>r</code> : 客户端套接字（在事件 loop 中）是可读的（readable）<code>w</code> : 客户端套接字（在事件 loop 中）是可写的（writeable）</p>
</li>
</ul>
<p>为了 debug 的需要，经常会对域进行添加和删除，一个安全的 Redis 客户端应该可以对 <code>CLIENT LIST</code> 的输出进行相应的处理（parse），比如忽略不存在的域，跳过未知域，诸如此类。</p>
<h2 id="6-CLIENT-SETNAME"><a href="#6-CLIENT-SETNAME" class="headerlink" title="6.CLIENT SETNAME"></a>6.CLIENT SETNAME</h2><p><strong>CLIENT SETNAME connection-name</strong></p>
<p>为当前连接分配一个名字。</p>
<p>这个名字会显示在 <em>CLIENT LIST</em> 命令的结果中， 用于识别当前正在与服务器进行连接的客户端。</p>
<p>举个例子， 在使用 Redis 构建队列（queue）时， 可以根据连接负责的任务（role）， 为信息生产者（producer）和信息消费者（consumer）分别设置不同的名字。</p>
<p>名字使用 Redis 的字符串类型来保存， 最大可以占用 512 MB 。 另外， 为了避免和 <em>CLIENT LIST</em> 命令的输出格式发生冲突， 名字里不允许使用空格。</p>
<p>要移除一个连接的名字， 可以将连接的名字设为空字符串 <code>&quot;&quot;</code> 。</p>
<p>使用 <em>CLIENT GETNAME</em> 命令可以取出连接的名字。</p>
<p>新创建的连接默认是没有名字的。</p>
<p>在 Redis 应用程序发生连接泄漏时，为连接设置名字是一种很好的 debug 手段。</p>
<ul>
<li><p><strong>可用版本</strong></p>
<blockquote>
<p>= 2.6.9</p>
</blockquote>
</li>
<li><p><strong>时间复杂度</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值</strong></p>
<p>设置成功时返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"># 新连接默认没有名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT GETNAME</span><br><span class="line">(nil)</span><br><span class="line"></span><br><span class="line"># 设置名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT SETNAME hello-world-connection</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"># 返回名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT GETNAME</span><br><span class="line">&quot;hello-world-connection&quot;</span><br><span class="line"></span><br><span class="line"># 在客户端列表中查看</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT LIST</span><br><span class="line">addr&#x3D;127.0.0.1:36851</span><br><span class="line">fd&#x3D;5</span><br><span class="line">name&#x3D;hello-world-connection     # &lt;- 名字</span><br><span class="line">age&#x3D;51</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"># 清除名字</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT SETNAME        # 只用空格是不行的！</span><br><span class="line">(error) ERR Syntax error, try CLIENT (LIST | KILL ip:port)</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT SETNAME &quot;&quot;     # 必须双引号显示包围</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CLIENT GETNAME        # 清除完毕</span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<h2 id="7-CONFIG-GET"><a href="#7-CONFIG-GET" class="headerlink" title="7.CONFIG GET"></a>7.CONFIG GET</h2><p><strong>CONFIG GET parameter</strong></p>
<p>CONFIG GET 命令用于取得运行中的 Redis 服务器的配置参数(configuration parameters)，在 Redis 2.4 版本中， 有部分参数没有办法用 <code>CONFIG GET</code> 访问，但是在最新的 Redis 2.6 版本中，所有配置参数都已经可以用 <code>CONFIG GET</code> 访问了。</p>
<p>CONFIG GET 接受单个参数 <code>parameter</code> 作为搜索关键字，查找所有匹配的配置参数，其中参数和值以“键-值对”(key-value pairs)的方式排列。</p>
<p>比如执行 <code>CONFIG GET s*</code> 命令，服务器就会返回所有以 <code>s</code> 开头的配置参数及参数的值：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CONFIG GET s*</span><br><span class="line">1) &quot;save&quot;                       # 参数名：save</span><br><span class="line">2) &quot;900 1 300 10 60 10000&quot;      # save 参数的值</span><br><span class="line">3) &quot;slave-serve-stale-data&quot;     # 参数名： slave-serve-stale-data</span><br><span class="line">4) &quot;yes&quot;                        # slave-serve-stale-data 参数的值</span><br><span class="line">5) &quot;set-max-intset-entries&quot;     # ...</span><br><span class="line">6) &quot;512&quot;</span><br><span class="line">7) &quot;slowlog-log-slower-than&quot;</span><br><span class="line">8) &quot;1000&quot;</span><br><span class="line">9) &quot;slowlog-max-len&quot;</span><br><span class="line">10) &quot;1000&quot;</span><br></pre></td></tr></table></figure>

<p>如果你只是寻找特定的某个参数的话，你当然也可以直接指定参数的名字：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CONFIG GET slowlog-max-len</span><br><span class="line">1) &quot;slowlog-max-len&quot;</span><br><span class="line">2) &quot;1000&quot;</span><br></pre></td></tr></table></figure>

<p>使用命令 <code>CONFIG GET *</code> ，可以列出 <code>CONFIG GET</code> 命令支持的所有参数：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CONFIG GET *</span><br><span class="line">1) &quot;dir&quot;</span><br><span class="line">2) &quot;&#x2F;var&#x2F;lib&#x2F;redis&quot;</span><br><span class="line">3) &quot;dbfilename&quot;</span><br><span class="line">4) &quot;dump.rdb&quot;</span><br><span class="line">5) &quot;requirepass&quot;</span><br><span class="line">6) (nil)</span><br><span class="line">7) &quot;masterauth&quot;</span><br><span class="line">8) (nil)</span><br><span class="line">9) &quot;maxmemory&quot;</span><br><span class="line">10) &quot;0&quot;</span><br><span class="line">11) &quot;maxmemory-policy&quot;</span><br><span class="line">12) &quot;volatile-lru&quot;</span><br><span class="line">13) &quot;maxmemory-samples&quot;</span><br><span class="line">14) &quot;3&quot;</span><br><span class="line">15) &quot;timeout&quot;</span><br><span class="line">16) &quot;0&quot;</span><br><span class="line">17) &quot;appendonly&quot;</span><br><span class="line">18) &quot;no&quot;</span><br><span class="line"># ...</span><br><span class="line">49) &quot;loglevel&quot;</span><br><span class="line">50) &quot;verbose&quot;</span><br></pre></td></tr></table></figure>

<p>所有被 <code>CONFIG SET</code> 所支持的配置参数都可以在配置文件 redis.conf 中找到，不过 <code>CONFIG GET</code> 和 <code>CONFIG SET</code> 使用的格式和 redis.conf 文件所使用的格式有以下两点不同：</p>
<ul>
<li><code>10kb</code> 、 <code>2gb</code> 这些在配置文件中所使用的储存单位缩写，不可以用在 <code>CONFIG</code> 命令中， <code>CONFIG SET</code> 的值只能通过数字值显式地设定。</li>
</ul>
<p>  像 <code>CONFIG SET xxx 1k</code> 这样的命令是错误的，正确的格式是 <code>CONFIG SET xxx 1000</code> 。</p>
<ul>
<li><code>save</code> 选项在 redis.conf 中是用多行文字储存的，但在 <code>CONFIG GET</code> 命令中，它只打印一行文字。</li>
</ul>
<p>  以下是 <code>save</code> 选项在 redis.conf 文件中的表示：</p>
<p>  <code>save 900 1</code></p>
<p>  <code>save 300 10</code></p>
<p>  <code>save 60 10000</code></p>
<p>  但是 <code>CONFIG GET</code> 命令的输出只有一行：</p>
<p>  <code>redis&gt; CONFIG GET save</code></p>
<p>  <code>1) &quot;save&quot;</code></p>
<p>  <code>2) &quot;900 1 300 10 60 10000&quot;</code></p>
<p>  上面 <code>save</code> 参数的三个值表示：在 900 秒内最少有 1 个 key 被改动，或者 300 秒内最少有 10 个 key 被改动，又或者 60 秒内最少有 1000 个 key 被改动，以上三个条件随便满足一个，就触发一次保存操作。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>给定配置参数的值。</p>
</li>
</ul>
<h2 id="8-CONFIG-RESETSTAT"><a href="#8-CONFIG-RESETSTAT" class="headerlink" title="8.CONFIG RESETSTAT"></a>8.CONFIG RESETSTAT</h2><p><strong>CONFIG RESETSTAT</strong></p>
<p>重置 [<em>INFO</em> 命令中的某些统计数据，包括：</p>
<ul>
<li><p>Keyspace hits (键空间命中次数)</p>
</li>
<li><p>Keyspace misses (键空间不命中次数)</p>
</li>
<li><p>Number of commands processed (执行命令的次数)</p>
</li>
<li><p>Number of connections received (连接服务器的次数)</p>
</li>
<li><p>Number of expired keys (过期key的数量)</p>
</li>
<li><p>Number of rejected connections (被拒绝的连接数量)</p>
</li>
<li><p>Latest fork(2) time(最后执行 fork(2) 的时间)</p>
</li>
<li><p>The <code>aof_delayed_fsync</code> counter(<code>aof_delayed_fsync</code> 计数器的值)</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br></pre></td><td class="code"><pre><span class="line"># 重置前</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; INFO</span><br><span class="line"># Server</span><br><span class="line">redis_version:2.5.3</span><br><span class="line">redis_git_sha1:d0407c2d</span><br><span class="line">redis_git_dirty:0</span><br><span class="line">arch_bits:32</span><br><span class="line">multiplexing_api:epoll</span><br><span class="line">gcc_version:4.6.3</span><br><span class="line">process_id:11095</span><br><span class="line">run_id:ef1f6b6c7392e52d6001eaf777acbe547d1192e2</span><br><span class="line">tcp_port:6379</span><br><span class="line">uptime_in_seconds:6</span><br><span class="line">uptime_in_days:0</span><br><span class="line">lru_clock:1205426</span><br><span class="line"></span><br><span class="line"># Clients</span><br><span class="line">connected_clients:1</span><br><span class="line">client_longest_output_list:0</span><br><span class="line">client_biggest_input_buf:0</span><br><span class="line">blocked_clients:0</span><br><span class="line"></span><br><span class="line"># Memory</span><br><span class="line">used_memory:331076</span><br><span class="line">used_memory_human:323.32K</span><br><span class="line">used_memory_rss:1568768</span><br><span class="line">used_memory_peak:293424</span><br><span class="line">used_memory_peak_human:286.55K</span><br><span class="line">used_memory_lua:16384</span><br><span class="line">mem_fragmentation_ratio:4.74</span><br><span class="line">mem_allocator:jemalloc-2.2.5</span><br><span class="line"></span><br><span class="line"># Persistence</span><br><span class="line">loading:0</span><br><span class="line">aof_enabled:0</span><br><span class="line">changes_since_last_save:0</span><br><span class="line">bgsave_in_progress:0</span><br><span class="line">last_save_time:1333260015</span><br><span class="line">last_bgsave_status:ok</span><br><span class="line">bgrewriteaof_in_progress:0</span><br><span class="line"></span><br><span class="line"># Stats</span><br><span class="line">total_connections_received:1</span><br><span class="line">total_commands_processed:0</span><br><span class="line">instantaneous_ops_per_sec:0</span><br><span class="line">rejected_connections:0</span><br><span class="line">expired_keys:0</span><br><span class="line">evicted_keys:0</span><br><span class="line">keyspace_hits:0</span><br><span class="line">keyspace_misses:0</span><br><span class="line">pubsub_channels:0</span><br><span class="line">pubsub_patterns:0</span><br><span class="line">latest_fork_usec:0</span><br><span class="line"></span><br><span class="line"># Replication</span><br><span class="line">role:master</span><br><span class="line">connected_slaves:0</span><br><span class="line"></span><br><span class="line"># CPU</span><br><span class="line">used_cpu_sys:0.01</span><br><span class="line">used_cpu_user:0.00</span><br><span class="line">used_cpu_sys_children:0.00</span><br><span class="line">used_cpu_user_children:0.00</span><br><span class="line"></span><br><span class="line"># Keyspace</span><br><span class="line">db0:keys&#x3D;20,expires&#x3D;0</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 重置</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; CONFIG RESETSTAT</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 重置后</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:6379&gt; INFO</span><br><span class="line"># Server</span><br><span class="line">redis_version:2.5.3</span><br><span class="line">redis_git_sha1:d0407c2d</span><br><span class="line">redis_git_dirty:0</span><br><span class="line">arch_bits:32</span><br><span class="line">multiplexing_api:epoll</span><br><span class="line">gcc_version:4.6.3</span><br><span class="line">process_id:11095</span><br><span class="line">run_id:ef1f6b6c7392e52d6001eaf777acbe547d1192e2</span><br><span class="line">tcp_port:6379</span><br><span class="line">uptime_in_seconds:134</span><br><span class="line">uptime_in_days:0</span><br><span class="line">lru_clock:1205438</span><br><span class="line"></span><br><span class="line"># Clients</span><br><span class="line">connected_clients:1</span><br><span class="line">client_longest_output_list:0</span><br><span class="line">client_biggest_input_buf:0</span><br><span class="line">blocked_clients:0</span><br><span class="line"></span><br><span class="line"># Memory</span><br><span class="line">used_memory:331076</span><br><span class="line">used_memory_human:323.32K</span><br><span class="line">used_memory_rss:1568768</span><br><span class="line">used_memory_peak:330280</span><br><span class="line">used_memory_peak_human:322.54K</span><br><span class="line">used_memory_lua:16384</span><br><span class="line">mem_fragmentation_ratio:4.74</span><br><span class="line">mem_allocator:jemalloc-2.2.5</span><br><span class="line"></span><br><span class="line"># Persistence</span><br><span class="line">loading:0</span><br><span class="line">aof_enabled:0</span><br><span class="line">changes_since_last_save:0</span><br><span class="line">bgsave_in_progress:0</span><br><span class="line">last_save_time:1333260015</span><br><span class="line">last_bgsave_status:ok</span><br><span class="line">bgrewriteaof_in_progress:0</span><br><span class="line"></span><br><span class="line"># Stats</span><br><span class="line">total_connections_received:0</span><br><span class="line">total_commands_processed:1</span><br><span class="line">instantaneous_ops_per_sec:0</span><br><span class="line">rejected_connections:0</span><br><span class="line">expired_keys:0</span><br><span class="line">evicted_keys:0</span><br><span class="line">keyspace_hits:0</span><br><span class="line">keyspace_misses:0</span><br><span class="line">pubsub_channels:0</span><br><span class="line">pubsub_patterns:0</span><br><span class="line">latest_fork_usec:0</span><br><span class="line"></span><br><span class="line"># Replication</span><br><span class="line">role:master</span><br><span class="line">connected_slaves:0</span><br><span class="line"></span><br><span class="line"># CPU</span><br><span class="line">used_cpu_sys:0.05</span><br><span class="line">used_cpu_user:0.02</span><br><span class="line">used_cpu_sys_children:0.00</span><br><span class="line">used_cpu_user_children:0.00</span><br><span class="line"></span><br><span class="line"># Keyspace</span><br><span class="line">db0:keys&#x3D;20,expires&#x3D;0</span><br></pre></td></tr></table></figure>

<h2 id="9-CONFIG-REWRITE"><a href="#9-CONFIG-REWRITE" class="headerlink" title="9.CONFIG REWRITE"></a>9.CONFIG REWRITE</h2><p><strong>CONFIG REWRITE</strong></p>
<p><em>CONFIG REWRITE</em> 命令对启动 Redis 服务器时所指定的 <code>redis.conf</code> 文件进行改写： 因为 <em>CONFIG SET</em> 命令可以对服务器的当前配置进行修改， 而修改后的配置可能和 <code>redis.conf</code> 文件中所描述的配置不一样， <em>CONFIG REWRITE</em> 的作用就是通过尽可能少的修改， 将服务器当前所使用的配置记录到 <code>redis.conf</code> 文件中。</p>
<p>重写会以非常保守的方式进行：</p>
<ul>
<li>原有 <code>redis.conf</code> 文件的整体结构和注释会被尽可能地保留。</li>
<li>如果一个选项已经存在于原有 <code>redis.conf</code> 文件中 ， 那么对该选项的重写会在选项原本所在的位置（行号）上进行。</li>
<li>如果一个选项不存在于原有 <code>redis.conf</code> 文件中， 并且该选项被设置为默认值， 那么重写程序不会将这个选项添加到重写后的 <code>redis.conf</code> 文件中。</li>
<li>如果一个选项不存在于原有 <code>redis.conf</code> 文件中， 并且该选项被设置为非默认值， 那么这个选项将被添加到重写后的 <code>redis.conf</code> 文件的末尾。</li>
<li>未使用的行会被留白。 比如说， 如果你在原有 <code>redis.conf</code> 文件上设置了数个关于 <code>save</code> 选项的参数， 但现在你将这些 <code>save</code> 参数的一个或全部都关闭了， 那么这些不再使用的参数原本所在的行就会变成空白的。</li>
</ul>
<p>即使启动服务器时所指定的 <code>redis.conf</code> 文件已经不再存在， <em>CONFIG REWRITE</em> 命令也可以重新构建并生成出一个新的 <code>redis.conf</code> 文件。</p>
<p>另一方面， 如果启动服务器时没有载入 <code>redis.conf</code> 文件， 那么执行 [<em>CONFIG REWRITE</em> 命令将引发一个错误。</p>
<h3 id="原子性重写"><a href="#原子性重写" class="headerlink" title="原子性重写"></a>原子性重写</h3><p>对 <code>redis.conf</code> 文件的重写是原子性的， 并且是一致的： 如果重写出错或重写期间服务器崩溃， 那么重写失败， 原有 <code>redis.conf</code> 文件不会被修改。 如果重写成功， 那么 <code>redis.conf</code> 文件为重写后的新文件。</p>
<h3 id="可用版本"><a href="#可用版本" class="headerlink" title="可用版本"></a>可用版本</h3><p>&gt;= 2.8.0</p>
<h3 id="返回值"><a href="#返回值" class="headerlink" title="返回值"></a>返回值</h3><p>一个状态值：如果配置重写成功则返回 <code>OK</code> ，失败则返回一个错误。</p>
<h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>以下是执行 <em>CONFIG REWRITE</em> 前， 被载入到 Redis 服务器的 <code>redis.conf</code> 文件中关于 <code>appendonly</code> 选项的设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># ... 其他选项</span><br><span class="line"></span><br><span class="line">appendonly no</span><br><span class="line"></span><br><span class="line"># ... 其他选项</span><br></pre></td></tr></table></figure>

<p>在执行以下命令之后：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; CONFIG GET appendonly           # appendonly 处于关闭状态</span><br><span class="line">1) &quot;appendonly&quot;</span><br><span class="line">2) &quot;no&quot;</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; CONFIG SET appendonly yes       # 打开 appendonly</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; CONFIG GET appendonly</span><br><span class="line">1) &quot;appendonly&quot;</span><br><span class="line">2) &quot;yes&quot;</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; CONFIG REWRITE                  # 将 appendonly 的修改写入到 redis.conf 中</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>重写后的 <code>redis.conf</code> 文件中的 <code>appendonly</code> 选项将被改写：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># ... 其他选项</span><br><span class="line"></span><br><span class="line">appendonly yes</span><br><span class="line"></span><br><span class="line"># ... 其他选项</span><br></pre></td></tr></table></figure>

<h2 id="10-CONFIG-SET"><a href="#10-CONFIG-SET" class="headerlink" title="10.CONFIG SET"></a>10.CONFIG SET</h2><p><strong>CONFIG SET parameter value</strong></p>
<p>CONFIG SET 命令可以动态地调整 Redis 服务器的配置(configuration)而无须重启。</p>
<p>你可以使用它修改配置参数，或者改变 Redis 的持久化(Persistence)方式。</p>
<p>CONFIG SET 可以修改的配置参数可以使用命令 <code>CONFIG GET *</code> 来列出，所有被 [CONFIG SET 修改的配置参数都会立即生效。</p>
<p>关于 CONFIG SET 命令的更多消息，请参见命令 <em>CONFIG GET</em> 的说明。</p>
<p>关于如何使用 CONFIG SET 命令修改 Redis 持久化方式，请参见 <em>Redis Persistence</em> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当设置成功时返回 <code>OK</code> ，否则返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CONFIG GET slowlog-max-len</span><br><span class="line">1) &quot;slowlog-max-len&quot;</span><br><span class="line">2) &quot;1024&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; CONFIG SET slowlog-max-len 10086</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; CONFIG GET slowlog-max-len</span><br><span class="line">1) &quot;slowlog-max-len&quot;</span><br><span class="line">2) &quot;10086&quot;</span><br></pre></td></tr></table></figure>

<h2 id="11-DBSIZE"><a href="#11-DBSIZE" class="headerlink" title="11.DBSIZE"></a>11.DBSIZE</h2><p><strong>DBSIZE</strong></p>
<p>返回当前数据库的 key 的数量。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当前数据库的 key 的数量。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DBSIZE</span><br><span class="line">(integer) 5</span><br><span class="line"></span><br><span class="line">redis&gt; SET new_key &quot;hello_moto&quot;     # 增加一个 key 试试</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DBSIZE</span><br><span class="line">(integer) 6</span><br></pre></td></tr></table></figure>

<h2 id="12-DEBUG-OBJECT"><a href="#12-DEBUG-OBJECT" class="headerlink" title="12.DEBUG OBJECT"></a>12.DEBUG OBJECT</h2><p><strong>DEBUG OBJECT key</strong></p>
<p>DEBUG OBJECT 是一个调试命令，它不应被客户端所使用。</p>
<p>查看 <em>OBJECT</em> 命令获取更多信息。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>当 <code>key</code> 存在时，返回有关信息。当 <code>key</code> 不存在时，返回一个错误。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DEBUG OBJECT my_pc</span><br><span class="line">Value at:0xb6838d20 refcount:1 encoding:raw serializedlength:9 lru:283790 lru_seconds_idle:150</span><br><span class="line"></span><br><span class="line">redis&gt; DEBUG OBJECT your_mac</span><br><span class="line">(error) ERR no such key</span><br></pre></td></tr></table></figure>

<h2 id="13-DEBUG-SEGFAULT"><a href="#13-DEBUG-SEGFAULT" class="headerlink" title="13.DEBUG SEGFAULT"></a>13.DEBUG SEGFAULT</h2><p><strong>DEBUG SEGFAULT</strong></p>
<p>执行一个不合法的内存访问从而让 Redis 崩溃，仅在开发时用于 BUG 模拟。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>无</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DEBUG SEGFAULT</span><br><span class="line">Could not connect to Redis at: Connection refused</span><br><span class="line"></span><br><span class="line">not connected&gt;</span><br></pre></td></tr></table></figure>

<h2 id="14-FLUSHALL"><a href="#14-FLUSHALL" class="headerlink" title="14.FLUSHALL"></a>14.FLUSHALL</h2><p><strong>FLUSHALL</strong></p>
<p>清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。</p>
<p>此命令从不失败。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>尚未明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DBSIZE            # 0 号数据库的 key 数量</span><br><span class="line">(integer) 9</span><br><span class="line"></span><br><span class="line">redis&gt; SELECT 1          # 切换到 1 号数据库</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis[1]&gt; DBSIZE         # 1 号数据库的 key 数量</span><br><span class="line">(integer) 6</span><br><span class="line"></span><br><span class="line">redis[1]&gt; flushall       # 清空所有数据库的所有 key</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis[1]&gt; DBSIZE         # 不但 1 号数据库被清空了</span><br><span class="line">(integer) 0</span><br><span class="line"></span><br><span class="line">redis[1]&gt; SELECT 0       # 0 号数据库(以及其他所有数据库)也一样</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DBSIZE</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="15-FLUSHDB"><a href="#15-FLUSHDB" class="headerlink" title="15.FLUSHDB"></a>15.FLUSHDB</h2><p><strong>FLUSHDB</strong></p>
<p>清空当前数据库中的所有 key。</p>
<p>此命令从不失败。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; DBSIZE    # 清空前的 key 数量</span><br><span class="line">(integer) 4</span><br><span class="line"></span><br><span class="line">redis&gt; FLUSHDB</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; DBSIZE    # 清空后的 key 数量</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<h2 id="16-INFO"><a href="#16-INFO" class="headerlink" title="16.INFO"></a>16.INFO</h2><p><strong>INFO [section]</strong></p>
<p>以一种易于解释（parse）且易于阅读的格式，返回关于 Redis 服务器的各种信息和统计数值。</p>
<p>通过给定可选的参数 <code>section</code> ，可以让命令只返回某一部分的信息：</p>
<ul>
<li><p><code>server</code> : 一般 Redis 服务器信息，包含以下域：</p>
<blockquote>
<ul>
<li><code>redis_version</code> : Redis 服务器版本</li>
<li><code>redis_git_sha1</code> : Git SHA1</li>
<li><code>redis_git_dirty</code> : Git dirty flag</li>
<li><code>os</code> : Redis 服务器的宿主操作系统</li>
<li><code>arch_bits</code> : 架构（32 或 64 位）</li>
<li><code>multiplexing_api</code> : Redis 所使用的事件处理机制</li>
<li><code>gcc_version</code> : 编译 Redis 时所使用的 GCC 版本</li>
<li><code>process_id</code> : 服务器进程的 PID</li>
<li><code>run_id</code> : Redis 服务器的随机标识符（用于 Sentinel 和集群）</li>
<li><code>tcp_port</code> : TCP/IP 监听端口</li>
<li><code>uptime_in_seconds</code> : 自 Redis 服务器启动以来，经过的秒数</li>
<li><code>uptime_in_days</code> : 自 Redis 服务器启动以来，经过的天数</li>
<li><code>lru_clock</code> : 以分钟为单位进行自增的时钟，用于 LRU 管理</li>
</ul>
</blockquote>
</li>
<li><p><code>clients</code> : 已连接客户端信息，包含以下域：</p>
<blockquote>
<ul>
<li><code>connected_clients</code> : 已连接客户端的数量（不包括通过从属服务器连接的客户端）</li>
<li><code>client_longest_output_list</code> : 当前连接的客户端当中，最长的输出列表</li>
<li><code>client_longest_input_buf</code> : 当前连接的客户端当中，最大输入缓存</li>
<li><code>blocked_clients</code> : 正在等待阻塞命令（BLPOP、BRPOP、BRPOPLPUSH）的客户端的数量</li>
</ul>
</blockquote>
</li>
<li><p><code>memory</code> : 内存信息，包含以下域：</p>
<blockquote>
<ul>
<li><code>used_memory</code> : 由 Redis 分配器分配的内存总量，以字节（byte）为单位</li>
<li><code>used_memory_human</code> : 以人类可读的格式返回 Redis 分配的内存总量</li>
<li><code>used_memory_rss</code> : 从操作系统的角度，返回 Redis 已分配的内存总量（俗称常驻集大小）。这个值和 <code>top</code> 、 <code>ps</code> 等命令的输出一致。</li>
<li><code>used_memory_peak</code> : Redis 的内存消耗峰值（以字节为单位）</li>
<li><code>used_memory_peak_human</code> : 以人类可读的格式返回 Redis 的内存消耗峰值</li>
<li><code>used_memory_lua</code> : Lua 引擎所使用的内存大小（以字节为单位）</li>
<li><code>mem_fragmentation_ratio</code> : <code>used_memory_rss</code> 和 <code>used_memory</code> 之间的比率</li>
<li><code>mem_allocator</code> : 在编译时指定的， Redis 所使用的内存分配器。可以是 libc 、 jemalloc 或者 tcmalloc 。</li>
</ul>
<p>在理想情况下， <code>used_memory_rss</code> 的值应该只比 <code>used_memory</code> 稍微高一点儿。</p>
<p>当 <code>rss &gt; used</code> ，且两者的值相差较大时，表示存在（内部或外部的）内存碎片。</p>
<p>内存碎片的比率可以通过 <code>mem_fragmentation_ratio</code> 的值看出。</p>
<p>当 <code>used &gt; rss</code> 时，表示 Redis 的部分内存被操作系统换出到交换空间了，在这种情况下，操作可能会产生明显的延迟。</p>
<p>Because Redis does not have control over how its allocations are mapped to memory pages, high <code>used_memory_rss</code> is often the result of a spike in memory usage.</p>
<p>当 Redis 释放内存时，分配器可能会，也可能不会，将内存返还给操作系统。</p>
<p>如果 Redis 释放了内存，却没有将内存返还给操作系统，那么 <code>used_memory</code> 的值可能和操作系统显示的 Redis 内存占用并不一致。</p>
<p>查看 <code>used_memory_peak</code> 的值可以验证这种情况是否发生。</p>
</blockquote>
</li>
<li><p><code>persistence</code> : <code>RDB</code> 和 <code>AOF</code> 的相关信息</p>
</li>
<li><p><code>stats</code> : 一般统计信息</p>
</li>
<li><p><code>replication</code> : 主/从复制信息</p>
</li>
<li><p><code>cpu</code> : CPU 计算量统计信息</p>
</li>
<li><p><code>commandstats</code> : Redis 命令统计信息</p>
</li>
<li><p><code>cluster</code> : Redis 集群信息</p>
</li>
<li><p><code>keyspace</code> : 数据库相关的统计信息</p>
</li>
</ul>
<p>除上面给出的这些值以外，参数还可以是下面这两个：</p>
<ul>
<li><code>all</code> : 返回所有信息</li>
<li><code>default</code> : 返回默认选择的信息</li>
</ul>
<p>当不带参数直接调用 INFO 命令时，使用 <code>default</code> 作为默认参数。</p>
<p>不同版本的 Redis 可能对返回的一些域进行了增加或删减。</p>
<p>因此，一个健壮的客户端程序在对 <em>INFO</em> 命令的输出进行分析时，应该能够跳过不认识的域，并且妥善地处理丢失不见的域。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>具体请参见下面的测试代码。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; INFO</span><br><span class="line"># Server</span><br><span class="line">redis_version:2.5.9</span><br><span class="line">redis_git_sha1:473f3090</span><br><span class="line">redis_git_dirty:0</span><br><span class="line">os:Linux 3.3.7-1-ARCH i686</span><br><span class="line">arch_bits:32</span><br><span class="line">multiplexing_api:epoll</span><br><span class="line">gcc_version:4.7.0</span><br><span class="line">process_id:8104</span><br><span class="line">run_id:bc9e20c6f0aac67d0d396ab950940ae4d1479ad1</span><br><span class="line">tcp_port:6379</span><br><span class="line">uptime_in_seconds:7</span><br><span class="line">uptime_in_days:0</span><br><span class="line">lru_clock:1680564</span><br><span class="line"></span><br><span class="line"># Clients</span><br><span class="line">connected_clients:1</span><br><span class="line">client_longest_output_list:0</span><br><span class="line">client_biggest_input_buf:0</span><br><span class="line">blocked_clients:0</span><br><span class="line"></span><br><span class="line"># Memory</span><br><span class="line">used_memory:439304</span><br><span class="line">used_memory_human:429.01K</span><br><span class="line">used_memory_rss:13897728</span><br><span class="line">used_memory_peak:401776</span><br><span class="line">used_memory_peak_human:392.36K</span><br><span class="line">used_memory_lua:20480</span><br><span class="line">mem_fragmentation_ratio:31.64</span><br><span class="line">mem_allocator:jemalloc-3.0.0</span><br><span class="line"></span><br><span class="line"># Persistence</span><br><span class="line">loading:0</span><br><span class="line">rdb_changes_since_last_save:0</span><br><span class="line">rdb_bgsave_in_progress:0</span><br><span class="line">rdb_last_save_time:1338011402</span><br><span class="line">rdb_last_bgsave_status:ok</span><br><span class="line">rdb_last_bgsave_time_sec:-1</span><br><span class="line">rdb_current_bgsave_time_sec:-1</span><br><span class="line">aof_enabled:0</span><br><span class="line">aof_rewrite_in_progress:0</span><br><span class="line">aof_rewrite_scheduled:0</span><br><span class="line">aof_last_rewrite_time_sec:-1</span><br><span class="line">aof_current_rewrite_time_sec:-1</span><br><span class="line"></span><br><span class="line"># Stats</span><br><span class="line">total_connections_received:1</span><br><span class="line">total_commands_processed:0</span><br><span class="line">instantaneous_ops_per_sec:0</span><br><span class="line">rejected_connections:0</span><br><span class="line">expired_keys:0</span><br><span class="line">evicted_keys:0</span><br><span class="line">keyspace_hits:0</span><br><span class="line">keyspace_misses:0</span><br><span class="line">pubsub_channels:0</span><br><span class="line">pubsub_patterns:0</span><br><span class="line">latest_fork_usec:0</span><br><span class="line"></span><br><span class="line"># Replication</span><br><span class="line">role:master</span><br><span class="line">connected_slaves:0</span><br><span class="line"></span><br><span class="line"># CPU</span><br><span class="line">used_cpu_sys:0.03</span><br><span class="line">used_cpu_user:0.01</span><br><span class="line">used_cpu_sys_children:0.00</span><br><span class="line">used_cpu_user_children:0.00</span><br><span class="line"></span><br><span class="line"># Keyspace</span><br></pre></td></tr></table></figure>

<h2 id="17-LASTSAVE"><a href="#17-LASTSAVE" class="headerlink" title="17.LASTSAVE"></a>17.LASTSAVE</h2><p><strong>LASTSAVE</strong></p>
<p>返回最近一次 Redis 成功将数据保存到磁盘上的时间，以 UNIX 时间戳格式表示。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个 UNIX 时间戳。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; LASTSAVE</span><br><span class="line">(integer) 1324043588</span><br></pre></td></tr></table></figure>

<h2 id="18-MONITOR"><a href="#18-MONITOR" class="headerlink" title="18.MONITOR"></a>18.MONITOR</h2><p><strong>MONITOR</strong></p>
<p>实时打印出 Redis 服务器接收到的命令，调试用。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; MONITOR</span><br><span class="line">OK</span><br><span class="line"># 以第一个打印值为例</span><br><span class="line"># 1378822099.421623 是时间戳</span><br><span class="line"># [0 127.0.0.1:56604] 中的 0 是数据库号码， 127... 是 IP 地址和端口</span><br><span class="line"># &quot;PING&quot; 是被执行的命令</span><br><span class="line">1378822099.421623 [0 127.0.0.1:56604] &quot;PING&quot;</span><br><span class="line">1378822105.089572 [0 127.0.0.1:56604] &quot;SET&quot; &quot;msg&quot; &quot;hello world&quot;</span><br><span class="line">1378822109.036925 [0 127.0.0.1:56604] &quot;SET&quot; &quot;number&quot; &quot;123&quot;</span><br><span class="line">1378822140.649496 [0 127.0.0.1:56604] &quot;SADD&quot; &quot;fruits&quot; &quot;Apple&quot; &quot;Banana&quot; &quot;Cherry&quot;</span><br><span class="line">1378822154.117160 [0 127.0.0.1:56604] &quot;EXPIRE&quot; &quot;msg&quot; &quot;10086&quot;</span><br><span class="line">1378822257.329412 [0 127.0.0.1:56604] &quot;KEYS&quot; &quot;*&quot;</span><br><span class="line">1378822258.690131 [0 127.0.0.1:56604] &quot;DBSIZE&quot;</span><br></pre></td></tr></table></figure>

<h2 id="19-PSYNC"><a href="#19-PSYNC" class="headerlink" title="19.PSYNC"></a>19.PSYNC</h2><p><strong>PSYNC <MASTER_RUN_ID> <OFFSET></strong></p>
<p>用于复制功能(replication)的内部命令。</p>
<p>更多信息请参考 <em>复制（Replication）</em> 文档。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.8.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>不明确</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; PSYNC ? -1</span><br><span class="line">&quot;REDIS0006\xfe\x00\x00\x02kk\x02vv\x00\x03msg\x05hello\xff\xc3\x96P\x12h\bK\xef&quot;</span><br></pre></td></tr></table></figure>

<h2 id="20-SAVE"><a href="#20-SAVE" class="headerlink" title="20.SAVE"></a>20.SAVE</h2><p><strong>SAVE</strong></p>
<p>SAVE 命令执行一个同步保存操作，将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。</p>
<p>一般来说，在生产环境很少执行 SAVE 操作，因为它会阻塞所有客户端，保存数据库的任务通常由 <em>BGSAVE</em> 命令异步地执行。然而，如果负责保存数据的后台子进程不幸出现问题时， SAVE 可以作为保存数据的最后手段来使用。</p>
<p>请参考文档： <em>Redis 的持久化运作方式(英文)</em> 以获取更多消息。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(N)， <code>N</code> 为要保存到数据库中的 key 的数量。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>保存成功时返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SAVE</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h2 id="21-SHUTDOWN"><a href="#21-SHUTDOWN" class="headerlink" title="21.SHUTDOWN"></a>21.SHUTDOWN</h2><p><strong>SHUTDOWN</strong></p>
<p>SHUTDOWN 命令执行以下操作：</p>
<ul>
<li>停止所有客户端</li>
<li>如果有至少一个保存点在等待，执行 [<em>SAVE</em> 命令</li>
<li>如果 AOF 选项被打开，更新 AOF 文件</li>
<li>关闭 redis 服务器(server)</li>
</ul>
<p>如果持久化被打开的话， SHUTDOWN 命令会保证服务器正常关闭而不丢失任何数据。</p>
<p>另一方面，假如只是单纯地执行 <em>SAVE</em> 命令，然后再执行 <em>QUIT</em> 命令，则没有这一保证 —— 因为在执行 <em>SAVE</em> 之后、执行 <em>QUIT</em> 之前的这段时间中间，其他客户端可能正在和服务器进行通讯，这时如果执行 [<em>QUIT</em> 就会造成数据丢失。</p>
<p><strong>SAVE 和 NOSAVE 修饰符</strong></p>
<p>通过使用可选的修饰符，可以修改 SHUTDOWN 命令的表现。比如说：</p>
<ul>
<li><p>执行 <code>SHUTDOWN SAVE</code> 会强制让数据库执行保存操作，即使没有设定(configure)保存点</p>
</li>
<li><p>执行 <code>SHUTDOWN NOSAVE</code> 会阻止数据库执行保存操作，即使已经设定有一个或多个保存点(你可以将这一用法看作是强制停止服务器的一个假想的 ABORT 命令)</p>
</li>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>执行失败时返回错误。执行成功时不返回任何信息，服务器和客户端的连接断开，客户端自动退出。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; PING</span><br><span class="line">PONG</span><br><span class="line"></span><br><span class="line">redis&gt; SHUTDOWN</span><br><span class="line"></span><br><span class="line">$</span><br><span class="line"></span><br><span class="line">$ redis</span><br><span class="line">Could not connect to Redis at: Connection refused</span><br><span class="line">not connected&gt;</span><br></pre></td></tr></table></figure>

<h2 id="22-SLAVEOF"><a href="#22-SLAVEOF" class="headerlink" title="22.SLAVEOF"></a>22.SLAVEOF</h2><ul>
<li><code>SLAVEOF host port</code></li>
</ul>
<p>SLAVEOF 命令用于在 Redis 运行时动态地修改复制(replication)功能的行为。</p>
<p>通过执行 <code>SLAVEOF host port</code> 命令，可以将当前服务器转变为指定服务器的从属服务器(slave server)。</p>
<p>如果当前服务器已经是某个主服务器(master server)的从属服务器，那么执行 <code>SLAVEOF host port</code> 将使当前服务器停止对旧主服务器的同步，丢弃旧数据集，转而开始对新主服务器进行同步。</p>
<p>另外，对一个从属服务器执行命令 <code>SLAVEOF NO ONE</code> 将使得这个从属服务器关闭复制功能，并从从属服务器转变回主服务器，原来同步所得的数据集<em>不会</em>被丢弃。</p>
<p>利用『 <code>SLAVEOF NO ONE</code> 不会丢弃同步所得数据集』这个特性，可以在主服务器失败的时候，将从属服务器用作新的主服务器，从而实现无间断运行。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p><code>SLAVEOF host port</code> ，O(N)， <code>N</code> 为要同步的数据数量。<code>SLAVEOF NO ONE</code> ， O(1) 。</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>总是返回 <code>OK</code> 。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SLAVEOF 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SLAVEOF NO ONE</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<h2 id="23-SLOWLOG"><a href="#23-SLOWLOG" class="headerlink" title="23.SLOWLOG"></a>23.SLOWLOG</h2><p><strong>SLOWLOG subcommand [argument]</strong></p>
<p><strong>什么是 SLOWLOG</strong></p>
<p>Slow log 是 Redis 用来记录查询执行时间的日志系统。</p>
<p>查询执行时间指的是不包括像客户端响应(talking)、发送回复等 IO 操作，而单单是执行一个查询命令所耗费的时间。</p>
<p>另外，slow log 保存在内存里面，读写速度非常快，因此你可以放心地使用它，不必担心因为开启 slow log 而损害 Redis 的速度。</p>
<p><strong>设置 SLOWLOG</strong></p>
<p>Slow log 的行为由两个配置参数(configuration parameter)指定，可以通过改写 redis.conf 文件或者用 <code>CONFIG GET</code> 和 <code>CONFIG SET</code> 命令对它们动态地进行修改。</p>
<p>第一个选项是 <code>slowlog-log-slower-than</code> ，它决定要对执行时间大于多少微秒(microsecond，1秒 = 1,000,000 微秒)的查询进行记录。</p>
<p>比如执行以下命令将让 slow log 记录所有查询时间大于等于 100 微秒的查询：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONFIG SET slowlog-log-slower-than 100</span><br></pre></td></tr></table></figure>

<p>而以下命令记录所有查询时间大于 1000 微秒的查询：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONFIG SET slowlog-log-slower-than 1000</span><br></pre></td></tr></table></figure>

<p>另一个选项是 <code>slowlog-max-len</code> ，它决定 slow log <em>最多</em>能保存多少条日志， slow log 本身是一个 FIFO 队列，当队列大小超过 <code>slowlog-max-len</code> 时，最旧的一条日志将被删除，而最新的一条日志加入到 slow log ，以此类推。</p>
<p>以下命令让 slow log 最多保存 1000 条日志：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONFIG SET slowlog-max-len 1000</span><br></pre></td></tr></table></figure>

<p>使用 <code>CONFIG GET</code> 命令可以查询两个选项的当前值：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; CONFIG GET slowlog-log-slower-than</span><br><span class="line">1) &quot;slowlog-log-slower-than&quot;</span><br><span class="line">2) &quot;1000&quot;</span><br><span class="line"></span><br><span class="line">redis&gt; CONFIG GET slowlog-max-len</span><br><span class="line">1) &quot;slowlog-max-len&quot;</span><br><span class="line">2) &quot;1000&quot;</span><br></pre></td></tr></table></figure>

<p><strong>查看 slow log</strong></p>
<p>要查看 slow log ，可以使用 <code>SLOWLOG GET</code> 或者 <code>SLOWLOG GET number</code> 命令，前者打印所有 slow log ，最大长度取决于 <code>slowlog-max-len</code> 选项的值，而 <code>SLOWLOG GET number</code> 则只打印指定数量的日志。</p>
<p>最新的日志会最先被打印：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"># 为测试需要，将 slowlog-log-slower-than 设成了 10 微秒</span><br><span class="line"></span><br><span class="line">redis&gt; SLOWLOG GET</span><br><span class="line">1) 1) (integer) 12                      # 唯一性(unique)的日志标识符</span><br><span class="line">   2) (integer) 1324097834              # 被记录命令的执行时间点，以 UNIX 时间戳格式表示</span><br><span class="line">   3) (integer) 16                      # 查询执行时间，以微秒为单位</span><br><span class="line">   4) 1) &quot;CONFIG&quot;                       # 执行的命令，以数组的形式排列</span><br><span class="line">      2) &quot;GET&quot;                          # 这里完整的命令是 CONFIG GET slowlog-log-slower-than</span><br><span class="line">      3) &quot;slowlog-log-slower-than&quot;</span><br><span class="line"></span><br><span class="line">2) 1) (integer) 11</span><br><span class="line">   2) (integer) 1324097825</span><br><span class="line">   3) (integer) 42</span><br><span class="line">   4) 1) &quot;CONFIG&quot;</span><br><span class="line">      2) &quot;GET&quot;</span><br><span class="line">      3) &quot;*&quot;</span><br><span class="line"></span><br><span class="line">3) 1) (integer) 10</span><br><span class="line">   2) (integer) 1324097820</span><br><span class="line">   3) (integer) 11</span><br><span class="line">   4) 1) &quot;CONFIG&quot;</span><br><span class="line">      2) &quot;GET&quot;</span><br><span class="line">      3) &quot;slowlog-log-slower-than&quot;</span><br><span class="line"></span><br><span class="line"># ...</span><br></pre></td></tr></table></figure>

<p>日志的唯一 id 只有在 Redis 服务器重启的时候才会重置，这样可以避免对日志的重复处理(比如你可能会想在每次发现新的慢查询时发邮件通知你)。</p>
<p><strong>查看当前日志的数量</strong></p>
<p>使用命令 <code>SLOWLOG LEN</code> 可以查看当前日志的数量。</p>
<p>请注意这个值和 <code>slower-max-len</code> 的区别，它们一个是当前日志的数量，一个是允许记录的最大日志的数量。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SLOWLOG LEN</span><br><span class="line">(integer) 14</span><br></pre></td></tr></table></figure>

<p><strong>清空日志</strong></p>
<p>使用命令 <code>SLOWLOG RESET</code> 可以清空 slow log 。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SLOWLOG LEN</span><br><span class="line">(integer) 14</span><br><span class="line"></span><br><span class="line">redis&gt; SLOWLOG RESET</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis&gt; SLOWLOG LEN</span><br><span class="line">(integer) 0</span><br></pre></td></tr></table></figure>

<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.2.12</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>取决于不同命令，返回不同的值。</p>
</li>
</ul>
<h2 id="24-SYNC"><a href="#24-SYNC" class="headerlink" title="24.SYNC"></a>24.SYNC</h2><p><strong>SYNC</strong></p>
<p>用于复制功能(replication)的内部命令。</p>
<p>更多信息请参考 <em>Redis 官网的 Replication 章节</em> 。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 1.0.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>不明确</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>不明确</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; SYNC</span><br><span class="line">&quot;REDIS0002\xfe\x00\x00\auser_id\xc0\x03\x00\anumbers\xc2\xf3\xe0\x01\x00\x00\tdb_number\xc0\x00\x00\x04name\x06huangz\x00\anew_key\nhello_moto\x00\bgreeting\nhello moto\x00\x05my_pc\bthinkpad\x00\x04lock\xc0\x01\x00\nlock_times\xc0\x04\xfe\x01\t\x04info\x19\x02\x04name\b\x00zhangyue\x03age\x02\x0022\xff\t\aooredis,\x03\x04name\a\x00ooredis\aversion\x03\x001.0\x06author\x06\x00huangz\xff\x00\tdb_number\xc0\x01\x00\x05greet\x0bhello world\x02\nmy_friends\x02\x05marry\x04jack\x00\x04name\x05value\xfe\x02\x0c\x01s\x12\x12\x00\x00\x00\r\x00\x00\x00\x02\x00\x00\x01a\x03\xc0f&#39;\xff\xff&quot;</span><br><span class="line">(1.90s)</span><br></pre></td></tr></table></figure>

<h2 id="25-TIME"><a href="#25-TIME" class="headerlink" title="25.TIME"></a>25.TIME</h2><p><strong>TIME</strong></p>
<p>返回当前服务器时间。</p>
<ul>
<li><p><strong>可用版本：</strong></p>
<blockquote>
<p>= 2.6.0</p>
</blockquote>
</li>
<li><p><strong>时间复杂度：</strong></p>
<p>O(1)</p>
</li>
<li><p><strong>返回值：</strong></p>
<p>一个包含两个字符串的列表： 第一个字符串是当前时间(以 UNIX 时间戳格式表示)，而第二个字符串是当前这一秒钟已经逝去的微秒数。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; TIME</span><br><span class="line">1) &quot;1332395997&quot;</span><br><span class="line">2) &quot;952581&quot;</span><br><span class="line">redis&gt; TIME</span><br><span class="line">1) &quot;1332395997&quot;</span><br><span class="line">2) &quot;953148&quot;</span><br></pre></td></tr></table></figure>

<h1 id="十二、集群教程"><a href="#十二、集群教程" class="headerlink" title="十二、集群教程"></a>十二、集群教程</h1><p>本文档翻译自 <a href="http://redis.io/topics/cluster-tutorial" target="_blank" rel="noopener">http://redis.io/topics/cluster-tutorial</a> 。</p>
<p>本文档是 Redis 集群的入门教程， 从用户的角度介绍了设置、测试和操作集群的方法。</p>
<p>本教程不包含晦涩难懂的分布式概念， 也没有像 <em>Redis 集群规范</em> 那样包含 Redis 集群的实现细节， 如果你打算深入地学习 Redis 集群的部署方法， 那么推荐你在阅读完这个教程之后， 再去看一看集群规范。</p>
<p><strong>Redis 集群目前仍处于 Alpha 测试版本</strong>， 如果在使用过程中发现任何问题， 请到 <em>Redis 的邮件列表</em> 发贴， 或者到 <em>Redis 的 Github 页面</em> 报告错误。</p>
<h2 id="集群简介"><a href="#集群简介" class="headerlink" title="集群简介"></a>集群简介</h2><p>Redis 集群是一个可以<strong>在多个 Redis 节点之间进行数据共享</strong>的设施（installation）。</p>
<p>Redis 集群不支持那些需要同时处理多个键的 Redis 命令， 因为执行这些命令需要在多个 Redis 节点之间移动数据， 并且在高负载的情况下， 这些命令将降低 Redis 集群的性能， 并导致不可预测的行为。</p>
<p>Redis 集群<strong>通过分区（partition）来提供一定程度的可用性</strong>（availability）： 即使集群中有一部分节点失效或者无法进行通讯， 集群也可以继续处理命令请求。</p>
<p>Redis 集群提供了以下两个好处：</p>
<ul>
<li>将数据自动切分（split）到多个节点的能力。</li>
<li>当集群中的一部分节点失效或者无法进行通讯时， 仍然可以继续处理命令请求的能力。</li>
</ul>
<h2 id="Redis-集群数据共享"><a href="#Redis-集群数据共享" class="headerlink" title="Redis 集群数据共享"></a>Redis 集群数据共享</h2><p>Redis 集群使用数据分片（sharding）而非一致性哈希（consistency hashing）来实现： 一个 Redis 集群包含 <code>16384</code> 个哈希槽（hash slot）， 数据库中的每个键都属于这 <code>16384</code> 个哈希槽的其中一个， 集群使用公式 <code>CRC16(key) % 16384</code> 来计算键 <code>key</code> 属于哪个槽， 其中 <code>CRC16(key)</code> 语句用于计算键 <code>key</code> 的 <em>CRC16 校验和</em>  。</p>
<p>集群中的每个节点负责处理一部分哈希槽。 举个例子， 一个集群可以有三个哈希槽， 其中：</p>
<ul>
<li>节点 A 负责处理 <code>0</code> 号至 <code>5500</code> 号哈希槽。</li>
<li>节点 B 负责处理 <code>5501</code> 号至 <code>11000</code> 号哈希槽。</li>
<li>节点 C 负责处理 <code>11001</code> 号至 <code>16384</code> 号哈希槽。</li>
</ul>
<p>这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说：</p>
<ul>
<li>如果用户将新节点 D 添加到集群中， 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。</li>
<li>与此类似， 如果用户要从集群中移除节点 A ， 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C ， 然后再移除空白（不包含任何哈希槽）的节点 A 就可以了。</li>
</ul>
<p>因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞， 所以无论是添加新节点还是移除已存在节点， 又或者改变某个节点包含的哈希槽数量， 都不会造成集群下线。</p>
<h2 id="Redis-集群中的主从复制"><a href="#Redis-集群中的主从复制" class="headerlink" title="Redis 集群中的主从复制"></a>Redis 集群中的主从复制</h2><p>为了使得集群在一部分节点下线或者无法与集群的大多数（majority）节点进行通讯的情况下， 仍然可以正常运作， Redis 集群对节点使用了主从复制功能： 集群中的每个节点都有 <code>1</code> 个至 <code>N</code> 个复制品（replica）， 其中一个复制品为主节点（master）， 而其余的 <code>N-1</code> 个复制品为从节点（slave）。</p>
<p>在之前列举的节点 A 、B 、C 的例子中， 如果节点 B 下线了， 那么集群将无法正常运行， 因为集群找不到节点来处理 <code>5501</code> 号至 <code>11000</code> 号的哈希槽。</p>
<p>另一方面， 假如在创建集群的时候（或者至少在节点 B 下线之前）， 我们为主节点 B 添加了从节点 B1 ， 那么当主节点 B 下线的时候， 集群就会将 B1 设置为新的主节点， 并让它代替下线的主节点 B ， 继续处理 <code>5501</code> 号至 <code>11000</code> 号的哈希槽， 这样集群就不会因为主节点 B 的下线而无法正常运作了。</p>
<p>不过如果节点 B 和 B1 都下线的话， Redis 集群还是会停止运作。</p>
<h2 id="Redis-集群的一致性保证（guarantee）"><a href="#Redis-集群的一致性保证（guarantee）" class="headerlink" title="Redis 集群的一致性保证（guarantee）"></a>Redis 集群的一致性保证（guarantee）</h2><p>Redis 集群<strong>不保证数据的强一致性</strong>（strong consistency）： 在特定条件下， Redis 集群可能会丢失已经被执行过的写命令。</p>
<p>使用异步复制（asynchronous replication）是 Redis 集群可能会丢失写命令的其中一个原因。 考虑以下这个写命令的例子：</p>
<ul>
<li>客户端向主节点 B 发送一条写命令。</li>
<li>主节点 B 执行写命令，并向客户端返回命令回复。</li>
<li>主节点 B 将刚刚执行的写命令复制给它的从节点 B1 、 B2 和 B3 。</li>
</ul>
<p>如你所见， 主节点对命令的复制工作发生在返回命令回复之后， 因为如果每次处理命令请求都需要等待复制操作完成的话， 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。</p>
<p>如果真的有必要的话， Redis 集群可能会在将来提供同步地（synchronou）执行写命令的方法。</p>
<p>Redis 集群另外一种可能会丢失命令的情况是， 集群出现网络分裂（network partition）， 并且一个客户端与至少包括一个主节点在内的少数（minority）实例被孤立。</p>
<p>举个例子， 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点， 其中 A 、B 、C 为主节点， 而 A1 、B1 、C1 分别为三个主节点的从节点， 另外还有一个客户端 Z1 。</p>
<p>假设集群中发生网络分裂， 那么集群可能会分裂为两方， 大多数（majority）的一方包含节点 A 、C 、A1 、B1 和 C1 ， 而少数（minority）的一方则包含节点 B 和客户端 Z1 。</p>
<p>在网络分裂期间， 主节点 B 仍然会接受 Z1 发送的写命令：</p>
<ul>
<li>如果网络分裂出现的时间很短， 那么集群会继续正常运行；</li>
<li>但是， 如果网络分裂出现的时间足够长， 使得大多数一方将从节点 B1 设置为新的主节点， 并使用 B1 来代替原来的主节点 B ， 那么 Z1 发送给主节点 B 的写命令将丢失。</li>
</ul>
<p>注意， 在网络分裂出现期间， 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的， 这一时间限制称为<strong>节点超时时间</strong>（node timeout）， 是 Redis 集群的一个重要的配置选项：</p>
<ul>
<li>对于大多数一方来说， 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群， 那么集群会将这个主节点视为下线， 并使用从节点来代替这个主节点继续工作。</li>
<li>对于少数一方， 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群， 那么它将停止处理写命令， 并向客户端报告错误。</li>
</ul>
<h2 id="创建并使用-Redis-集群"><a href="#创建并使用-Redis-集群" class="headerlink" title="创建并使用 Redis 集群"></a>创建并使用 Redis 集群</h2><p>Redis 集群由多个运行在集群模式（cluster mode）下的 Redis 实例组成， 实例的集群模式需要通过配置来开启， 开启集群模式的实例将可以使用集群特有的功能和命令。</p>
<p>以下是一个包含了最少选项的集群配置文件示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">port 7000</span><br><span class="line">cluster-enabled yes</span><br><span class="line">cluster-config-file nodes.conf</span><br><span class="line">cluster-node-timeout 5000</span><br><span class="line">appendonly yes</span><br></pre></td></tr></table></figure>

<p>文件中的 <code>cluster-enabled</code> 选项用于开实例的集群模式， 而 <code>cluster-conf-file</code> 选项则设定了保存节点配置文件的路径， 默认值为 <code>nodes.conf</code> 。</p>
<p>节点配置文件无须人为修改， 它由 Redis 集群在启动时创建， 并在有需要时自动进行更新。</p>
<p><strong>要让集群正常运作至少需要三个主节点</strong>， 不过在刚开始试用集群功能时， 强烈建议使用六个节点： 其中三个为主节点， 而其余三个则是各个主节点的从节点。</p>
<p>首先， 让我们进入一个新目录， 并创建六个以端口号为名字的子目录， 稍后我们在将每个目录中运行一个 Redis 实例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mkdir cluster-test</span><br><span class="line">cd cluster-test</span><br><span class="line">mkdir 7000 7001 7002 7003 7004 7005</span><br></pre></td></tr></table></figure>

<p>在文件夹 <code>7000</code> 至 <code>7005</code> 中， 各创建一个 <code>redis.conf</code> 文件， 文件的内容可以使用上面的示例配置文件， 但记得将配置中的端口号从 <code>7000</code> 改为与文件夹名字相同的号码。</p>
<p>现在， 从 [Redis Github 页面] 的 <code>unstable</code> 分支中取出最新的 Redis 源码， 编译出可执行文件 <code>redis-server</code> ， 并将文件复制到 <code>cluster-test</code> 文件夹， 然后使用类似以下命令， 在每个标签页中打开一个实例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd 7000</span><br><span class="line">..&#x2F;redis-server .&#x2F;redis.conf</span><br></pre></td></tr></table></figure>

<p>实例打印的日志显示， 因为 <code>nodes.conf</code> 文件不存在， 所以每个节点都为它自身指定了一个新的 ID ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I&#39;m 97a3a64667477371c4479320d683e4c8db5858b1</span><br></pre></td></tr></table></figure>

<p>实例会一直使用同一个 ID ， 从而在集群中保持一个独一无二（unique）的名字。</p>
<p>每个节点都使用 ID 而不是 IP 或者端口号来记录其他节点， 因为 IP 地址和端口号都可能会改变， 而这个独一无二的标识符（identifier）则会在节点的整个生命周期中一直保持不变。</p>
<p>我们将这个标识符称为<strong>节点 ID</strong>。</p>
<h2 id="创建集群"><a href="#创建集群" class="headerlink" title="创建集群"></a>创建集群</h2><p>现在我们已经有了六个正在运行中的 Redis 实例， 接下来我们需要使用这些实例来创建集群， 并为每个节点编写配置文件。</p>
<p>通过使用 Redis 集群命令行工具 <code>redis-trib</code> ， 编写节点配置文件的工作可以非常容易地完成： <code>redis-trib</code> 位于 Redis 源码的 <code>src</code> 文件夹中， 它是一个 Ruby 程序， 这个程序通过向实例发送特殊命令来完成创建新集群， 检查集群， 或者对集群进行重新分片（reshared）等工作。</p>
<p>我们需要执行以下命令来创建集群：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">.&#x2F;redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \</span><br><span class="line">127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005</span><br></pre></td></tr></table></figure>

<p>命令的意义如下：</p>
<ul>
<li>给定 <code>redis-trib.rb</code> 程序的命令是 <code>create</code> ， 这表示我们希望创建一个新的集群。</li>
<li>选项 <code>--replicas 1</code> 表示我们希望为集群中的每个主节点创建一个从节点。</li>
<li>之后跟着的其他参数则是实例的地址列表， 我们希望程序使用这些地址所指示的实例来创建新集群。</li>
</ul>
<p>简单来说， 以上命令的意思就是让 <code>redis-trib</code> 程序创建一个包含三个主节点和三个从节点的集群。</p>
<p>接着， <code>redis-trib</code> 会打印出一份预想中的配置给你看， 如果你觉得没问题的话， 就可以输入 <code>yes</code> ， <code>redis-trib</code> 就会将这份配置应用到集群当中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; Creating cluster</span><br><span class="line">Connecting to node 127.0.0.1:7000: OK</span><br><span class="line">Connecting to node 127.0.0.1:7001: OK</span><br><span class="line">Connecting to node 127.0.0.1:7002: OK</span><br><span class="line">Connecting to node 127.0.0.1:7003: OK</span><br><span class="line">Connecting to node 127.0.0.1:7004: OK</span><br><span class="line">Connecting to node 127.0.0.1:7005: OK</span><br><span class="line">&gt;&gt;&gt; Performing hash slots allocation on 6 nodes...</span><br><span class="line">Using 3 masters:</span><br><span class="line">127.0.0.1:7000</span><br><span class="line">127.0.0.1:7001</span><br><span class="line">127.0.0.1:7002</span><br><span class="line">127.0.0.1:7000 replica #1 is 127.0.0.1:7003</span><br><span class="line">127.0.0.1:7001 replica #1 is 127.0.0.1:7004</span><br><span class="line">127.0.0.1:7002 replica #1 is 127.0.0.1:7005</span><br><span class="line">M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000</span><br><span class="line">slots:0-5460 (5461 slots) master</span><br><span class="line">M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001</span><br><span class="line">slots:5461-10921 (5461 slots) master</span><br><span class="line">M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002</span><br><span class="line">slots:10922-16383 (5462 slots) master</span><br><span class="line">S: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003</span><br><span class="line">S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004</span><br><span class="line">S: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005</span><br><span class="line">Can I set the above configuration? (type &#39;yes&#39; to accept): yes</span><br></pre></td></tr></table></figure>

<p>输入 <code>yes</code> 并按下回车确认之后， 集群就会将配置应用到各个节点， 并连接起（join）各个节点 —— 也即是， 让各个节点开始互相通讯：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; Nodes configuration updated</span><br><span class="line">&gt;&gt;&gt; Sending CLUSTER MEET messages to join the cluster</span><br><span class="line">Waiting for the cluster to join...</span><br><span class="line">&gt;&gt;&gt; Performing Cluster Check (using node 127.0.0.1:7000)</span><br><span class="line">M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000</span><br><span class="line">slots:0-5460 (5461 slots) master</span><br><span class="line">M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001</span><br><span class="line">slots:5461-10921 (5461 slots) master</span><br><span class="line">M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002</span><br><span class="line">slots:10922-16383 (5462 slots) master</span><br><span class="line">M: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003</span><br><span class="line">slots: (0 slots) master</span><br><span class="line">M: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004</span><br><span class="line">slots: (0 slots) master</span><br><span class="line">M: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005</span><br><span class="line">slots: (0 slots) master</span><br><span class="line">[OK] All nodes agree about slots configuration.</span><br></pre></td></tr></table></figure>

<p>如果一切正常的话， <code>redis-trib</code> 将输出以下信息：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; Check for open slots...</span><br><span class="line">&gt;&gt;&gt; Check slots coverage...</span><br><span class="line">[OK] All 16384 slots covered.</span><br></pre></td></tr></table></figure>

<p>这表示集群中的 <code>16384</code> 个槽都有至少一个主节点在处理， 集群运作正常。</p>
<h2 id="集群的客户端"><a href="#集群的客户端" class="headerlink" title="集群的客户端"></a>集群的客户端</h2><p>Redis 集群现阶段的一个问题是客户端实现很少。 以下是一些我知道的实现：</p>
<ul>
<li><code>redis-rb-cluster</code> 是我（@antirez）编写的 Ruby 实现， 用于作为其他实现的参考。 该实现是对 <code>redis-rb</code> 的一个简单包装， 高效地实现了与集群进行通讯所需的最少语义（semantic）。</li>
<li><code>redis-py-cluster</code> 看上去是 <code>redis-rb-cluster</code> 的一个 Python 版本， 这个项目有一段时间没有更新了（最后一次提交是在六个月之前）， 不过可以将这个项目用作学习集群的起点。</li>
<li>流行的 Predis 曾经对早期的 Redis 集群有过一定的支持， 但我不确定它对集群的支持是否完整， 也不清楚它是否和最新版本的 Redis 集群兼容 （因为新版的 Redis 集群将槽的数量从 4k 改为 16k 了）。</li>
<li>Redis <code>unstable</code> 分支中的 <code>redis-cli</code> 程序实现了非常基本的集群支持， 可以使用命令 <code>redis-cli -c</code> 来启动。</li>
</ul>
<p>测试 Redis 集群比较简单的办法就是使用 <code>redis-rb-cluster</code> 或者 <code>redis-cli</code> ， 接下来我们将使用 <code>redis-cli</code> 为例来进行演示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli -c -p 7000</span><br><span class="line">redis 127.0.0.1:7000&gt; set foo bar</span><br><span class="line">-&gt; Redirected to slot [12182] located at 127.0.0.1:7002</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:7002&gt; set hello world</span><br><span class="line">-&gt; Redirected to slot [866] located at 127.0.0.1:7000</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:7000&gt; get foo</span><br><span class="line">-&gt; Redirected to slot [12182] located at 127.0.0.1:7002</span><br><span class="line">&quot;bar&quot;</span><br><span class="line"></span><br><span class="line">redis 127.0.0.1:7000&gt; get hello</span><br><span class="line">-&gt; Redirected to slot [866] located at 127.0.0.1:7000</span><br><span class="line">&quot;world&quot;</span><br></pre></td></tr></table></figure>

<p><code>redis-cli</code> 对集群的支持是非常基本的， 所以它总是依靠 Redis 集群节点来将它转向（redirect）至正确的节点。</p>
<p>一个真正的（serious）集群客户端应该做得比这更好： 它应该用缓存记录起哈希槽与节点地址之间的映射（map）， 从而直接将命令发送到正确的节点上面。</p>
<p>这种映射只会在集群的配置出现某些修改时变化， 比如说， 在一次故障转移（failover）之后， 或者系统管理员通过添加节点或移除节点来修改了集群的布局（layout）之后， 诸如此类。</p>
<h2 id="使用-redis-rb-cluster-编写一个示例应用"><a href="#使用-redis-rb-cluster-编写一个示例应用" class="headerlink" title="使用 redis-rb-cluster 编写一个示例应用"></a>使用 <code>redis-rb-cluster</code> 编写一个示例应用</h2><p>在展示如何使用集群进行故障转移、重新分片等操作之前， 我们需要创建一个示例应用， 了解一些与 Redis 集群客户端进行交互的基本方法。</p>
<p>在运行示例应用的过程中， 我们会尝试让节点进入失效状态， 又或者开始一次重新分片， 以此来观察 Redis 集群在真实世界运行时的表现， 并且为了让这个示例尽可能地有用， 我们会让这个应用向集群进行写操作。</p>
<p>本节将通过两个示例应用来展示 <code>redis-rb-cluster</code> 的基本用法， 以下是本节的第一个示例应用， 它是一个名为 [example.rb] 的文件， 包含在[redis-rb-cluster 项目里面]：</p>
<p>| <code>1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30</code> | <code>require &#39;./cluster&#39;  startup_nodes = [     {:host =&gt; &quot;127.0.0.1&quot;, :port =&gt; 7000},     {:host =&gt; &quot;127.0.0.1&quot;, :port =&gt; 7001} ] rc = RedisCluster.new(startup_nodes,32,:timeout =&gt; 0.1)  last = false  while not last     begin         last = rc.get(&quot;__last__&quot;)         last = 0 if !last     rescue =&gt; e         puts &quot;error #{e.to_s}&quot;         sleep 1     end end  ((last.to_i+1)..1000000000).each{|x|     begin         rc.set(&quot;foo#{x}&quot;,x)         puts rc.get(&quot;foo#{x}&quot;)         rc.set(&quot;__last__&quot;,x)     rescue =&gt; e         puts &quot;error #{e.to_s}&quot;     end     sleep 0.1 }</code> |<br>| ———————————————————— | ———————————————————— |<br>|                                                              |                                                              |</p>
<p>这个应用所做的工作非常简单： 它不断地以 <code>foo&lt;number&gt;</code> 为键， <code>number</code> 为值， 使用 [<em>SET</em> 命令向数据库设置键值对。</p>
<p>如果我们执行这个应用的话， 应用将按顺序执行以下命令：</p>
<ul>
<li><code>SET foo0 0</code></li>
<li><code>SET foo1 1</code></li>
<li><code>SET foo2 2</code></li>
<li>诸如此类。。。</li>
</ul>
<p>代码中的每个集群操作都使用一个 <code>begin</code> 和 <code>rescue</code> 代码块（block）包裹着， 因为我们希望在代码出错时， 将错误打印到终端上面， 而不希望应用因为异常（exception）而退出。</p>
<p>代码的<strong>第七行</strong>是代码中第一个有趣的地方， 它创建了一个 Redis 集群对象， 其中创建对象所使用的参数及其意义如下：</p>
<ul>
<li>第一个参数是记录了启动节点的 <code>startup_nodes</code> 列表， 列表中包含了两个集群节点的地址。</li>
<li>第二个参数指定了对于集群中的各个不同的节点， Redis 集群对象可以获得（take）的最大连接数 （maximum number of connections this object is allowed to take）。</li>
<li>第三个参数 <code>timeout</code> 指定了一个命令在执行多久之后， 才会被看作是执行失败。</li>
</ul>
<p>记住， 启动列表中并不需要包含所有集群节点的地址， 但这些地址中至少要有一个是有效的（reachable）： 一旦 <code>redis-rb-cluster</code> 成功连接上集群中的某个节点时， 集群节点列表就会被自动更新， 任何真正的（serious）的集群客户端都应该这样做。</p>
<p>现在， 程序创建的 Redis 集群对象实例被保存到 <code>rc</code> 变量里面， 我们可以将这个对象当作普通 Redis 对象实例来使用。</p>
<p>在<strong>十一至十九行</strong>， 我们先尝试阅读计数器中的值， 如果计数器不存在的话， 我们才将计数器初始化为 <code>0</code> ： 通过将计数值保存到 Redis 的计数器里面， 我们可以在示例重启之后， 仍然继续之前的执行过程， 而不必每次重启之后都从 <code>foo0</code> 开始重新设置键值对。</p>
<p>为了让程序在集群下线的情况下， 仍然不断地尝试读取计数器的值， 我们将读取操作包含在了一个 <code>while</code> 循环里面， 一般的应用程序并不需要如此小心。</p>
<p><strong>二十一至三十行</strong>是程序的主循环， 这个循环负责设置键值对， 并在设置出错时打印错误信息。</p>
<p>程序在主循环的末尾添加了一个 <code>sleep</code> 调用， 让写操作的执行速度变慢， 帮助执行示例的人更容易看清程序的输出。</p>
<p>执行 <code>example.rb</code> 程序将产生以下输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ruby .&#x2F;example.rb</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>这个程序并不是十分有趣， 稍后我们就会看到一个更有趣的集群应用示例， 不过在此之前， 让我们先使用这个示例来演示集群的重新分片操作。</p>
<h2 id="对集群进行重新分片"><a href="#对集群进行重新分片" class="headerlink" title="对集群进行重新分片"></a>对集群进行重新分片</h2><p>现在， 让我们来试试对集群进行重新分片操作。</p>
<p>在执行重新分片的过程中， 请让你的 <code>example.rb</code> 程序处于运行状态， 这样你就会看到， 重新分片并不会对正在运行的集群程序产生任何影响， 你也可以考虑将 <code>example.rb</code> 中的 <code>sleep</code> 调用删掉， 从而让重新分片操作在近乎真实的写负载下执行。</p>
<p>重新分片操作基本上就是将某些节点上的哈希槽移动到另外一些节点上面， 和创建集群一样， 重新分片也可以使用 <code>redis-trib</code> 程序来执行。</p>
<p>执行以下命令可以开始一次重新分片操作：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br></pre></td></tr></table></figure>

<p>你只需要指定集群中其中一个节点的地址， <code>redis-trib</code> 就会自动找到集群中的其他节点。</p>
<p>目前 <code>redis-trib</code> 只能在管理员的协助下完成重新分片的工作， 要让 <code>redis-trib</code> 自动将哈希槽从一个节点移动到另一个节点， 目前来说还做不到 （不过实现这个功能并不难）。</p>
<p>执行 <code>redis-trib</code> 的第一步就是设定你打算移动的哈希槽的数量：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br><span class="line">Connecting to node 127.0.0.1:7000: OK</span><br><span class="line">Connecting to node 127.0.0.1:7002: OK</span><br><span class="line">Connecting to node 127.0.0.1:7005: OK</span><br><span class="line">Connecting to node 127.0.0.1:7001: OK</span><br><span class="line">Connecting to node 127.0.0.1:7003: OK</span><br><span class="line">Connecting to node 127.0.0.1:7004: OK</span><br><span class="line">&gt;&gt;&gt; Performing Cluster Check (using node 127.0.0.1:7000)</span><br><span class="line">M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000</span><br><span class="line">slots:0-5460 (5461 slots) master</span><br><span class="line">M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002</span><br><span class="line">slots:10922-16383 (5462 slots) master</span><br><span class="line">S: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001</span><br><span class="line">slots:5461-10921 (5461 slots) master</span><br><span class="line">S: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">[OK] All nodes agree about slots configuration.</span><br><span class="line">&gt;&gt;&gt; Check for open slots...</span><br><span class="line">&gt;&gt;&gt; Check slots coverage...</span><br><span class="line">[OK] All 16384 slots covered.</span><br><span class="line">How many slots do you want to move (from 1 to 16384)? 1000</span><br></pre></td></tr></table></figure>

<p>我们将打算移动的槽数量设置为 <code>1000</code> 个， 如果 <code>example.rb</code> 程序一直运行着的话， 现在 <code>1000</code> 个槽里面应该有不少键了。</p>
<p>除了移动的哈希槽数量之外， <code>redis-trib</code> 还需要知道重新分片的目标（target node）， 也即是， 负责接收这 <code>1000</code> 个哈希槽的节点。</p>
<p>指定目标需要使用节点的 ID ， 而不是 IP 地址和端口。 比如说， 我们打算使用集群的第一个主节点来作为目标， 它的 IP 地址和端口是 <code>127.0.0.1:7000</code> ， 而节点 ID 则是 <code>9991306f0e50640a5684f1958fd754b38fa034c9</code> ， 那么我们应该向 <code>redis-trib</code> 提供节点的 ID ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br><span class="line">...</span><br><span class="line">What is the receiving node ID? 9991306f0e50640a5684f1958fd754b38fa034c9</span><br></pre></td></tr></table></figure>

<p><code>redis-trib</code> 会打印出集群中所有节点的 ID ， 并且我们也可以通过执行以下命令来获得节点的运行 ID ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-cli -p 7000 cluster nodes | grep myself</span><br><span class="line">9991306f0e50640a5684f1958fd754b38fa034c9 :0 myself,master - 0 0 0 connected 0-5460</span><br></pre></td></tr></table></figure>

<p>接着， <code>redis-trib</code> 会向你询问重新分片的源节点（source node）， 也即是， 要从哪个节点中取出 <code>1000</code> 个哈希槽， 并将这些槽移动到目标节点上面。</p>
<p>如果我们不打算从特定的节点上取出指定数量的哈希槽， 那么可以向 <code>redis-trib</code> 输入 <code>all</code> ， 这样的话， 集群中的所有主节点都会成为源节点， <code>redis-trib</code> 将从各个源节点中各取出一部分哈希槽， 凑够 <code>1000</code> 个， 然后移动到目标节点上面：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br><span class="line">...</span><br><span class="line">Please enter all the source node IDs.</span><br><span class="line">Type &#39;all&#39; to use all the nodes as source nodes for the hash slots.</span><br><span class="line">Type &#39;done&#39; once you entered all the source nodes IDs.</span><br><span class="line">Source node #1:all</span><br></pre></td></tr></table></figure>

<p>输入 <code>all</code> 并按下回车之后， <code>redis-trib</code> 将打印出哈希槽的移动计划， 如果你觉得没问题的话， 就可以输入 <code>yes</code> 并再次按下回车：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br><span class="line">...</span><br><span class="line">Moving slot 11421 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a</span><br><span class="line">Moving slot 11422 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a</span><br><span class="line">Moving slot 5461 from e68e52cee0550f558b03b342f2f0354d2b8a083b</span><br><span class="line">Moving slot 5469 from e68e52cee0550f558b03b342f2f0354d2b8a083b</span><br><span class="line">...</span><br><span class="line">Moving slot 5959 from e68e52cee0550f558b03b342f2f0354d2b8a083b</span><br><span class="line">Do you want to proceed with the proposed reshard plan (yes&#x2F;no)? yes</span><br></pre></td></tr></table></figure>

<p>输入 <code>yes</code> 并使用按下回车之后， <code>redis-trib</code> 就会正式开始执行重新分片操作， 将指定的哈希槽从源节点一个个地移动到目标节点上面：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb reshard 127.0.0.1:7000</span><br><span class="line">...</span><br><span class="line">Moving slot 5934 from 127.0.0.1:7001 to 127.0.0.1:7000:</span><br><span class="line">Moving slot 5935 from 127.0.0.1:7001 to 127.0.0.1:7000:</span><br><span class="line">Moving slot 5936 from 127.0.0.1:7001 to 127.0.0.1:7000:</span><br><span class="line">Moving slot 5937 from 127.0.0.1:7001 to 127.0.0.1:7000:</span><br><span class="line">...</span><br><span class="line">Moving slot 5959 from 127.0.0.1:7001 to 127.0.0.1:7000:</span><br></pre></td></tr></table></figure>

<p>在重新分片的过程中， <code>example.rb</code> 应该可以继续正常运行， 不会出现任何问题。</p>
<p>在重新分片操作执行完毕之后， 可以使用以下命令来检查集群是否正常：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;redis-trib.rb check 127.0.0.1:7000</span><br><span class="line">Connecting to node 127.0.0.1:7000: OK</span><br><span class="line">Connecting to node 127.0.0.1:7002: OK</span><br><span class="line">Connecting to node 127.0.0.1:7005: OK</span><br><span class="line">Connecting to node 127.0.0.1:7001: OK</span><br><span class="line">Connecting to node 127.0.0.1:7003: OK</span><br><span class="line">Connecting to node 127.0.0.1:7004: OK</span><br><span class="line">&gt;&gt;&gt; Performing Cluster Check (using node 127.0.0.1:7000)</span><br><span class="line">M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000</span><br><span class="line">slots:0-5959,10922-11422 (6461 slots) master</span><br><span class="line">M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002</span><br><span class="line">slots:11423-16383 (4961 slots) master</span><br><span class="line">S: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001</span><br><span class="line">slots:5960-10921 (4962 slots) master</span><br><span class="line">S: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004</span><br><span class="line">slots: (0 slots) slave</span><br><span class="line">[OK] All nodes agree about slots configuration.</span><br><span class="line">&gt;&gt;&gt; Check for open slots...</span><br><span class="line">&gt;&gt;&gt; Check slots coverage...</span><br><span class="line">[OK] All 16384 slots covered.</span><br></pre></td></tr></table></figure>

<p>根据检查结果显示， 集群运作正常。</p>
<p>需要注意的就是， 在三个主节点中， 节点 <code>127.0.0.1:7000</code> 包含了 <code>6461</code> 个哈希槽， 而节点 <code>127.0.0.1:7001</code> 和节点 <code>127.0.0.1:7002</code> 都只包含了 <code>4961</code> 个哈希槽， 因为后两者都将自己的 <code>500</code> 个哈希槽移动到了节点 <code>127.0.0.1:7000</code> 。</p>
<h2 id="一个更有趣的示例应用"><a href="#一个更有趣的示例应用" class="headerlink" title="一个更有趣的示例应用"></a>一个更有趣的示例应用</h2><p>我们在前面使用的示例程序 <code>example.rb</code> 并不是十分有趣， 因为它只是不断地对集群进行写入， 但并不检查写入结果是否正确。 比如说， 集群可能会错误地将 <code>example.rb</code> 发送的所有 [<em>SET</em> 命令都改成了 <code>SET foo 42</code> ， 但因为 <code>example.rb</code> 并不检查写入后的值， 所以它不会意识到集群实际上写入的值是错误的。</p>
<p>因为这个原因， [redis-rb-cluster 项目]包含了一个名为 [consistency-test.rb] 的示例应用， 这个应用比起 <code>example.rb</code> 有趣得多： 它创建了多个计数器（默认为 <code>1000</code> 个）， 并通过发送 [<em>INCR</em> 命令来增加这些计数器的值。</p>
<p>在增加计数器值的同时， <code>consistency-test.rb</code> 还执行以下操作：</p>
<ul>
<li>每次使用 <em>INCR</em> 命令更新一个计数器时， 应用会记录下计数器执行 <em>INCR</em> 命令之后应该有的值。 举个例子， 如果计数器的起始值为 <code>0</code> ， 而这次是程序第 <code>50</code> 次向它发送 <em>INCR</em> 命令， 那么计数器的值应该是 <code>50</code> 。</li>
<li>在每次发送 <em>INCR</em> 命令之前， 程序会随机从集群中读取一个计数器的值， 并将它与自己记录的值进行对比， 看两个值是否相同。</li>
</ul>
<p>换句话说， 这个程序是一个一致性检查器（consistency checker）： 如果集群在执行 <em>INCR</em> 命令的过程中， 丢失了某条 <em>INCR</em> 命令， 又或者多执行了某条客户端没有确认到的 <em>INCR</em> 命令， 那么检查器将察觉到这一点 —— 在前一种情况中， <code>consistency-test.rb</code> 记录的计数器值将比集群记录的计数器值要大； 而在后一种情况中， <code>consistency-test.rb</code> 记录的计数器值将比集群记录的计数器值要小。</p>
<p>运行 <code>consistency-test</code> 程序将产生类似以下的输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ ruby consistency-test.rb</span><br><span class="line">925 R (0 err) | 925 W (0 err) |</span><br><span class="line">5030 R (0 err) | 5030 W (0 err) |</span><br><span class="line">9261 R (0 err) | 9261 W (0 err) |</span><br><span class="line">13517 R (0 err) | 13517 W (0 err) |</span><br><span class="line">17780 R (0 err) | 17780 W (0 err) |</span><br><span class="line">22025 R (0 err) | 22025 W (0 err) |</span><br><span class="line">25818 R (0 err) | 25818 W (0 err) |</span><br></pre></td></tr></table></figure>

<p>每行输出都打印了程序执行的读取次数和写入次数， 以及执行操作的过程中因为集群不可用而产生的错误数。</p>
<p>如果程序察觉了不一致的情况出现， 它将在输出行的末尾显式不一致的详细情况。</p>
<p>比如说， 如果我们在 <code>consistency-test.rb</code> 运行的过程中， 手动修改某个计数器的值：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ redis 127.0.0.1:7000&gt; set key_217 0</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>那么 <code>consistency-test.rb</code> 将向我们报告不一致情况：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">(in the other tab I see...)</span><br><span class="line"></span><br><span class="line">94774 R (0 err) | 94774 W (0 err) |</span><br><span class="line">98821 R (0 err) | 98821 W (0 err) |</span><br><span class="line">102886 R (0 err) | 102886 W (0 err) | 114 lost |</span><br><span class="line">107046 R (0 err) | 107046 W (0 err) | 114 lost |</span><br></pre></td></tr></table></figure>

<p>在我们修改计数器值的时候， 计数器的正确值是 <code>114</code> （执行了 <code>114</code> 次 [<em>INCR</em> 命令）， 因为我们将计数器的值设成了 <code>0</code> ， 所以 <code>consistency-test.rb</code> 会向我们报告说丢失了 <code>114</code> 个 [<em>INCR</em> 命令。</p>
<p>因为这个示例程序具有一致性检查功能， 所以我们用它来测试 Redis 集群的故障转移操作。</p>
<h2 id="故障转移测试"><a href="#故障转移测试" class="headerlink" title="故障转移测试"></a>故障转移测试</h2><p>在执行本节操作的过程中， 请一直运行 <code>consistency-test</code> 程序。</p>
<p>要触发一次故障转移， 最简单的办法就是令集群中的某个主节点进入下线状态。</p>
<p>首先用以下命令列出集群中的所有主节点：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli -p 7000 cluster nodes | grep master</span><br><span class="line">3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921</span><br><span class="line">2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383</span><br><span class="line">97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422</span><br></pre></td></tr></table></figure>

<p>通过命令输出， 我们知道端口号为 <code>7000</code> 、 <code>7001</code> 和 <code>7002</code> 的节点都是主节点， 然后我们可以通过向端口号为 <code>7002</code> 的主节点发送 <em>DEBUG SEGFAULT</em> 命令， 让这个主节点崩溃：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli -p 7002 debug segfault</span><br><span class="line">Error: Server closed the connection</span><br></pre></td></tr></table></figure>

<p>现在， 切换到运行着 <code>consistency-test</code> 的标签页， 可以看到， <code>consistency-test</code> 在 <code>7002</code> 下线之后的一段时间里将产生大量的错误警告信息：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">18849 R (0 err) | 18849 W (0 err) |</span><br><span class="line">23151 R (0 err) | 23151 W (0 err) |</span><br><span class="line">27302 R (0 err) | 27302 W (0 err) |</span><br><span class="line"></span><br><span class="line">... many error warnings here ...</span><br><span class="line"></span><br><span class="line">29659 R (578 err) | 29660 W (577 err) |</span><br><span class="line">33749 R (578 err) | 33750 W (577 err) |</span><br><span class="line">37918 R (578 err) | 37919 W (577 err) |</span><br><span class="line">42077 R (578 err) | 42078 W (577 err) |</span><br></pre></td></tr></table></figure>

<p>从 <code>consistency-test</code> 的这段输出可以看到， 集群在执行故障转移期间， 总共丢失了 <code>578</code> 个读命令和 <code>577</code> 个写命令， 但是并没有产生任何数据不一致。</p>
<p>这听上去可能有点奇怪， 因为在教程的开头我们提到过， Redis 使用的是异步复制， 在执行故障转移期间， 集群可能会丢失写命令。</p>
<p>但是在实际上， 丢失命令的情况并不常见， 因为 Redis 几乎是同时执行将命令回复发送给客户端， 以及将命令复制给从节点这两个操作， 所以实际上造成命令丢失的时间窗口是非常小的。</p>
<p>不过， 尽管出现的几率不高， 但丢失命令的情况还是有可能会出现的， 所以我们对 Redis 集群不能提供强一致性的这一描述仍然是正确的。</p>
<p>现在， 让我们使用 <code>cluster nodes</code> 命令， 查看集群在执行故障转移操作之后， 主从节点的布局情况：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli -p 7000 cluster nodes</span><br><span class="line">3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected</span><br><span class="line">a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected</span><br><span class="line">97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422</span><br><span class="line">3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383</span><br><span class="line">3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921</span><br><span class="line">2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected</span><br></pre></td></tr></table></figure>

<p>我重启了之前下线的 <code>127.0.0.1:7002</code> 节点， 该节点已经从原来的主节点变成了从节点， 而现在集群中的三个主节点分别是 <code>127.0.0.1:7000</code> 、 <code>127.0.0.1:7001</code> 和 <code>127.0.0.1:7005</code> ， 其中 <code>127.0.0.1:7005</code> 就是因为 <code>127.0.0.1:7002</code> 下线而变成主节点的。</p>
<p><code>cluster nodes</code> 命令的输出有点儿复杂， 它的每一行都是由以下信息组成的：</p>
<ul>
<li>节点 ID ：例如 <code>3fc783611028b1707fd65345e763befb36454d73</code> 。</li>
<li><code>ip:port</code> ：节点的 IP 地址和端口号， 例如 <code>127.0.0.1:7000</code> ， 其中 <code>:0</code> 表示的是客户端当前连接的 IP 地址和端口号。</li>
<li><code>flags</code> ：节点的角色（例如 <code>master</code> 、 <code>slave</code> 、 <code>myself</code> ）以及状态（例如 <code>fail</code> ，等等）。</li>
<li>如果节点是一个从节点的话， 那么跟在 <code>flags</code> 之后的将是主节点的节点 ID ： 例如 <code>127.0.0.1:7002</code> 的主节点的节点 ID 就是 <code>3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e</code> 。</li>
<li>集群最近一次向节点发送 <em>PING</em> 命令之后， 过去了多长时间还没接到回复。</li>
<li>节点最近一次返回 <code>PONG</code> 回复的时间。</li>
<li>节点的配置纪元（configuration epoch）：详细信息请参考 [<em>Redis 集群规范</em> 。</li>
<li>本节点的网络连接情况：例如 <code>connected</code> 。</li>
<li>节点目前包含的槽：例如 <code>127.0.0.1:7001</code> 目前包含号码为 <code>5960</code> 至 <code>10921</code> 的哈希槽。</li>
</ul>
<h2 id="添加新节点到集群"><a href="#添加新节点到集群" class="headerlink" title="添加新节点到集群"></a>添加新节点到集群</h2><p>根据新添加节点的种类， 我们需要用两种方法来将新节点添加到集群里面：</p>
<ul>
<li>如果要添加的新节点是一个主节点， 那么我们需要创建一个空节点（empty node）， 然后将某些哈希桶移动到这个空节点里面。</li>
<li>另一方面， 如果要添加的新节点是一个从节点， 那么我们需要将这个新节点设置为集群中某个节点的复制品（replica）。</li>
</ul>
<p>本节将对以上两种情况进行介绍， 首先介绍主节点的添加方法， 然后再介绍从节点的添加方法。</p>
<p>无论添加的是那种节点， 第一步要做的总是添加一个空节点。</p>
<p>我们可以继续使用之前启动 <code>127.0.0.1:7000</code> 、 <code>127.0.0.1:7001</code> 等节点的方法， 创建一个端口号为 <code>7006</code> 的新节点， 使用的配置文件也和之前一样， 只是记得要将配置中的端口号改为 <code>7000</code> 。</p>
<p>以下是启动端口号为 <code>7006</code> 的新节点的详细步骤：</p>
<ol>
<li>在终端里创建一个新的标签页。</li>
<li>进入 <code>cluster-test</code> 文件夹。</li>
<li>创建并进入 <code>7006</code> 文件夹。</li>
<li>将 <code>redis.conf</code> 文件复制到 <code>7006</code> 文件夹里面，然后将配置中的端口号选项改为 <code>7006</code> 。</li>
<li>使用命令 <code>../../redis-server redis.conf</code> 启动节点。</li>
</ol>
<p>如果一切正常， 那么节点应该会正确地启动。</p>
<p>接下来， 执行以下命令， 将这个新节点添加到集群里面：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.&#x2F;redis-trib.rb addnode 127.0.0.1:7006 127.0.0.1:7000</span><br></pre></td></tr></table></figure>

<p>命令中的 <code>addnode</code> 表示我们要让 <code>redis-trib</code> 将一个节点添加到集群里面， <code>addnode</code> 之后跟着的是新节点的 IP 地址和端口号， 再之后跟着的是集群中任意一个已存在节点的 IP 地址和端口号， 这里我们使用的是 <code>127.0.0.1:7000</code> 。</p>
<p>通过 <code>cluster nodes</code> 命令， 我们可以确认新节点 <code>127.0.0.1:7006</code> 已经被添加到集群里面了：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:7006&gt; cluster nodes</span><br><span class="line">3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921</span><br><span class="line">3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected</span><br><span class="line">f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected</span><br><span class="line">2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected</span><br><span class="line">a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected</span><br><span class="line">97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422</span><br><span class="line">3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383</span><br></pre></td></tr></table></figure>

<p>新节点现在已经连接上了集群， 成为集群的一份子， 并且可以对客户端的命令请求进行转向了， 但是和其他主节点相比， 新节点还有两点区别：</p>
<ul>
<li>新节点没有包含任何数据， 因为它没有包含任何哈希桶。</li>
<li>尽管新节点没有包含任何哈希桶， 但它仍然是一个主节点， 所以在集群需要将某个从节点升级为新的主节点时， 这个新节点不会被选中。</li>
</ul>
<p>接下来， 只要使用 <code>redis-trib</code> 程序， 将集群中的某些哈希桶移动到新节点里面， 新节点就会成为真正的主节点了。</p>
<p>因为使用 <code>redis-trib</code> 移动哈希桶的方法在前面已经介绍过， 所以这里就不再重复介绍了。</p>
<p>现在， 让我们来看看， 将一个新节点转变为某个主节点的复制品（也即是从节点）的方法。</p>
<p>举个例子， 如果我们打算让新节点成为 <code>127.0.0.1:7005</code> 的从节点， 那么我们只要用客户端连接上新节点， 然后执行以下命令就可以了：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis 127.0.0.1:7006&gt; cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e</span><br></pre></td></tr></table></figure>

<p>其中命令提供的 <code>3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e</code> 就是主节点 <code>127.0.0.1:7005</code> 的节点 ID 。</p>
<p>执行 <code>cluster replicate</code> 命令之后， 我们可以使用以下命令来确认 <code>127.0.0.1:7006</code> 已经成为了 ID 为 <code>3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e</code> 的节点的从节点：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e</span><br><span class="line">f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected</span><br><span class="line">2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected</span><br></pre></td></tr></table></figure>

<p><code>3c3a0c...</code> 现在有两个从节点， 一个从节点的端口号为 <code>7002</code> ， 而另一个从节点的端口号为 <code>7006</code> 。</p>
<h2 id="移除一个节点"><a href="#移除一个节点" class="headerlink" title="移除一个节点"></a>移除一个节点</h2><p>未完待续。</p>
<h1 id="十二、Redis-集群规范"><a href="#十二、Redis-集群规范" class="headerlink" title="十二、Redis 集群规范"></a>十二、Redis 集群规范</h1><p>本文档翻译自 <a href="http://redis.io/topics/cluster-spec" target="_blank" rel="noopener">http://redis.io/topics/cluster-spec</a> 。</p>
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>这个文档是正在开发中的 Redis 集群功能的规范（specification）文档， 文档分为两个部分：</p>
<ul>
<li>第一部分介绍目前已经在 <code>unstable</code> 分支中实现了的那些功能。</li>
<li>第二部分介绍目前仍未实现的那些功能。</li>
</ul>
<p>文档各个部分的内容可能会随着集群功能的设计修改而发生改变， 其中， 未实现功能发生修改的几率比已实现功能发生修改的几率要高。</p>
<p>这个规范包含了编写客户端库（client library）所需的全部知识， 不过请注意， 这里列出的一部分细节可能会在未来发生变化。</p>
<h2 id="什么是-Redis-集群？"><a href="#什么是-Redis-集群？" class="headerlink" title="什么是 Redis 集群？"></a>什么是 Redis 集群？</h2><p>Redis 集群是一个分布式（distributed）、容错（fault-tolerant）的 Redis 实现， 集群可以使用的功能是普通单机 Redis 所能使用的功能的一个子集（subset）。</p>
<p>Redis 集群中不存在中心（central）节点或者代理（proxy）节点， 集群的其中一个主要设计目标是达到线性可扩展性（linear scalability）。</p>
<p>Redis 集群为了保证一致性（consistency）而牺牲了一部分容错性： 系统会在保证对网络断线（net split）和节点失效（node failure）具有有限（limited）抵抗力的前提下， 尽可能地保持数据的一致性。</p>
<p>集群将节点失效视为网络断线的其中一种特殊情况。</p>
<p>集群的容错功能是通过使用主节点（master）和从节点（slave）两种角色（role）的节点（node）来实现的：</p>
<ul>
<li>主节点和从节点使用完全相同的服务器实现， 它们的功能（functionally）也完全一样， 但从节点通常仅用于替换失效的主节点。</li>
<li>不过， 如果不需要保证“先写入，后读取”操作的一致性（read-after-write consistency）， 那么可以使用从节点来执行只读查询。</li>
</ul>
<h2 id="Redis-集群实现的功能子集"><a href="#Redis-集群实现的功能子集" class="headerlink" title="Redis 集群实现的功能子集"></a>Redis 集群实现的功能子集</h2><p>Redis 集群实现了单机 Redis 中， 所有处理单个数据库键的命令。</p>
<p>针对多个数据库键的复杂计算操作， 比如集合的并集操作、合集操作没有被实现， 那些理论上需要使用多个节点的多个数据库键才能完成的命令也没有被实现。</p>
<p>在将来， 用户也许可以通过 [<em>MIGRATE COPY</em> 命令， 在集群的计算节点（computation node）中执行针对多个数据库键的只读操作， 但集群本身不会去实现那些需要将多个数据库键在多个节点中移来移去的复杂多键命令。</p>
<p>Redis 集群不像单机 Redis 那样支持多数据库功能， 集群只使用默认的 <code>0</code> 号数据库， 并且不能使用 <em>SELECT</em> 命令。</p>
<h2 id="Redis-集群协议中的客户端和服务器"><a href="#Redis-集群协议中的客户端和服务器" class="headerlink" title="Redis 集群协议中的客户端和服务器"></a>Redis 集群协议中的客户端和服务器</h2><p>Redis 集群中的节点有以下责任：</p>
<ul>
<li>持有键值对数据。</li>
<li>记录集群的状态，包括键到正确节点的映射（mapping keys to right nodes）。</li>
<li>自动发现其他节点，识别工作不正常的节点，并在有需要时，在从节点中选举出新的主节点。</li>
</ul>
<p>为了执行以上列出的任务， 集群中的每个节点都与其他节点建立起了“集群连接（cluster bus）”， 该连接是一个 TCP 连接， 使用二进制协议进行通讯。</p>
<p>节点之间使用 [Gossip 协议] 来进行以下工作：</p>
<ul>
<li>传播（propagate）关于集群的信息，以此来发现新的节点。</li>
<li>向其他节点发送 <code>PING</code> 数据包，以此来检查目标节点是否正常运作。</li>
<li>在特定事件发生时，发送集群信息。</li>
</ul>
<p>除此之外， 集群连接还用于在集群中发布或订阅信息。</p>
<p>因为集群节点不能代理（proxy）命令请求， 所以客户端应该在节点返回 <code>-MOVED</code> 或者 <code>-ASK</code> 转向（redirection）错误时， 自行将命令请求转发至其他节点。</p>
<p>因为客户端可以自由地向集群中的任何一个节点发送命令请求， 并可以在有需要时， 根据转向错误所提供的信息， 将命令转发至正确的节点， 所以在理论上来说， 客户端是无须保存集群状态信息的。</p>
<p>不过， 如果客户端可以将键和节点之间的映射信息保存起来， 可以有效地减少可能出现的转向次数， 籍此提升命令执行的效率。</p>
<h2 id="键分布模型"><a href="#键分布模型" class="headerlink" title="键分布模型"></a>键分布模型</h2><p>Redis 集群的键空间被分割为 <code>16384</code> 个槽（slot）， 集群的最大节点数量也是 <code>16384</code> 个。</p>
<p>推荐的最大节点数量为 1000 个左右。</p>
<p>每个主节点都负责处理 <code>16384</code> 个哈希槽的其中一部分。</p>
<p>当我们说一个集群处于“稳定”（stable）状态时， 指的是集群没有在执行重配置（reconfiguration）操作， 每个哈希槽都只由一个节点进行处理。</p>
<p>重配置指的是将某个/某些槽从一个节点移动到另一个节点。</p>
<p>一个主节点可以有任意多个从节点， 这些从节点用于在主节点发生网络断线或者节点失效时， 对主节点进行替换。</p>
<p>以下是负责将键映射到槽的算法：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HASH_SLOT &#x3D; CRC16(key) mod 16384</span><br></pre></td></tr></table></figure>

<p>以下是该算法所使用的参数：</p>
<ul>
<li>算法的名称: XMODEM (又称 ZMODEM 或者 CRC-16/ACORN)</li>
<li>结果的长度: 16 位</li>
<li>多项数（poly）: 1021 (也即是 <code>x16 + x12 + x5 + 1</code>)</li>
<li>初始化值: <code>0000</code></li>
<li>反射输入字节（Reflect Input byte）: <code>False</code></li>
<li>发射输出 CRC （Reflect Output CRC）: <code>False</code></li>
<li>用于 CRC 输出值的异或常量（Xor constant to output CRC）: <code>0000</code></li>
<li>该算法对于输入 <code>&quot;123456789&quot;</code> 的输出: <code>31C3</code></li>
</ul>
<p>附录 A 中给出了集群所使用的 CRC16 算法的实现。</p>
<p>CRC16 算法所产生的 16 位输出中的 14 位会被用到。</p>
<p>在我们的测试中， CRC16 算法可以很好地将各种不同类型的键平稳地分布到 <code>16384</code> 个槽里面。</p>
<h2 id="集群节点属性"><a href="#集群节点属性" class="headerlink" title="集群节点属性"></a>集群节点属性</h2><p>每个节点在集群中都有一个独一无二的 ID ， 该 ID 是一个十六进制表示的 160 位随机数， 在节点第一次启动时由 <code>/dev/urandom</code> 生成。</p>
<p>节点会将它的 ID 保存到配置文件， 只要这个配置文件不被删除， 节点就会一直沿用这个 ID 。</p>
<p>节点 ID 用于标识集群中的每个节点。 一个节点可以改变它的 IP 和端口号， 而不改变节点 ID 。 集群可以自动识别出 IP/端口号的变化， 并将这一信息通过 Gossip 协议广播给其他节点知道。</p>
<p>以下是每个节点都有的关联信息， 并且节点会将这些信息发送给其他节点：</p>
<ul>
<li>节点所使用的 IP 地址和 TCP 端口号。</li>
<li>节点的标志（flags）。</li>
<li>节点负责处理的哈希槽。</li>
<li>节点最近一次使用集群连接发送 <code>PING</code> 数据包（packet）的时间。</li>
<li>节点最近一次在回复中接收到 <code>PONG</code> 数据包的时间。</li>
<li>集群将该节点标记为下线的时间。</li>
<li>该节点的从节点数量。</li>
<li>如果该节点是从节点的话，那么它会记录主节点的节点 ID 。 如果这是一个主节点的话，那么主节点 ID 这一栏的值为 <code>0000000</code> 。</li>
</ul>
<p>以上信息的其中一部分可以通过向集群中的任意节点（主节点或者从节点都可以）发送 <code>CLUSTER NODES</code> 命令来获得。</p>
<p>以下是一个向集群中的主节点发送 <code>CLUSTER NODES</code> 命令的例子， 该集群由三个节点组成：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ redis-cli cluster nodes</span><br><span class="line">d1861060fe6a534d42d8a19aeb36600e18785e04 :0 myself - 0 1318428930 connected 0-1364</span><br><span class="line">3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 connected 1365-2729</span><br><span class="line">d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 connected 2730-4095</span><br></pre></td></tr></table></figure>

<p>在上面列出的三行信息中， 从左到右的各个域分别是： 节点 ID ， IP 地址和端口号， 标志（flag）， 最后发送 <code>PING</code> 的时间， 最后接收 <code>PONG</code> 的时间， 连接状态， 节点负责处理的槽。</p>
<h2 id="节点握手（已实现）"><a href="#节点握手（已实现）" class="headerlink" title="节点握手（已实现）"></a>节点握手（已实现）</h2><p>节点总是应答（accept）来自集群连接端口的连接请求， 并对接收到的 <code>PING</code> 数据包进行回复， 即使这个 <code>PING</code> 数据包来自不可信的节点。</p>
<p>然而， 除了 <code>PING</code> 之外， 节点会拒绝其他所有并非来自集群节点的数据包。</p>
<p>要让一个节点承认另一个节点同属于一个集群， 只有以下两种方法：</p>
<ul>
<li>一个节点可以通过向另一个节点发送 <code>MEET</code> 信息， 来强制让接收信息的节点承认发送信息的节点为集群中的一份子。 一个节点仅在管理员显式地向它发送 <code>CLUSTER MEET ip port</code> 命令时， 才会向另一个节点发送 <code>MEET</code> 信息。</li>
<li>另外， 如果一个可信节点向另一个节点传播第三者节点的信息， 那么接收信息的那个节点也会将第三者节点识别为集群中的一份子。 也即是说， 如果 A 认识 B ， B 认识 C ， 并且 B 向 A 传播关于 C 的信息， 那么 A 也会将 C 识别为集群中的一份子， 并尝试连接 C 。</li>
</ul>
<p>这意味着如果我们将一个/一些新节点添加到一个集群中， 那么这个/这些新节点最终会和集群中已有的其他所有节点连接起来。</p>
<p>这说明只要管理员使用 <code>CLUSTER MEET</code> 命令显式地指定了可信关系， 集群就可以自动发现其他节点。</p>
<p>这种节点识别机制通过防止不同的 Redis 集群因为 IP 地址变更或者其他网络事件的发生而产生意料之外的联合（mix）， 从而使得集群更具健壮性。</p>
<p>当节点的网络连接断开时， 它会主动连接其他已知的节点。</p>
<h2 id="MOVED-转向"><a href="#MOVED-转向" class="headerlink" title="MOVED 转向"></a>MOVED 转向</h2><p>一个 Redis 客户端可以向集群中的任意节点（包括从节点）发送命令请求。 节点会对命令请求进行分析， 如果该命令是集群可以执行的命令， 那么节点会查找这个命令所要处理的键所在的槽。</p>
<p>如果要查找的哈希槽正好就由接收到命令的节点负责处理， 那么节点就直接执行这个命令。</p>
<p>另一方面， 如果所查找的槽不是由该节点处理的话， 节点将查看自身内部所保存的哈希槽到节点 ID 的映射记录， 并向客户端回复一个 <code>MOVED</code> 错误。</p>
<p>以下是一个 <code>MOVED</code> 错误的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">GET x</span><br><span class="line"></span><br><span class="line">-MOVED 3999 127.0.0.1:6381</span><br></pre></td></tr></table></figure>

<p>错误信息包含键 <code>x</code> 所属的哈希槽 <code>3999</code> ， 以及负责处理这个槽的节点的 IP 和端口号 <code>127.0.0.1:6381</code> 。 客户端需要根据这个 IP 和端口号， 向所属的节点重新发送一次 <em>GET</em> 命令请求。</p>
<p>注意， 即使客户端在重新发送 <em>GET</em> 命令之前， 等待了非常久的时间， 以至于集群又再次更改了配置， 使得节点 <code>127.0.0.1:6381</code> 已经不再处理槽 <code>3999</code> ， 那么当客户端向节点 <code>127.0.0.1:6381</code> 发送 <em>GET</em> 命令的时候， 节点将再次向客户端返回 <code>MOVED</code> 错误， 指示现在负责处理槽 <code>3999</code> 的节点。</p>
<p>虽然我们用 ID 来标识集群中的节点， 但是为了让客户端的转向操作尽可能地简单， 节点在 <code>MOVED</code> 错误中直接返回目标节点的 IP 和端口号， 而不是目标节点的 ID 。</p>
<p>虽然不是必须的， 但一个客户端应该记录（memorize）下“槽 <code>3999</code> 由节点 <code>127.0.0.1:6381</code> 负责处理“这一信息， 这样当再次有命令需要对槽 <code>3999</code> 执行时， 客户端就可以加快寻找正确节点的速度。</p>
<p>注意， 当集群处于稳定状态时， 所有客户端最终都会保存有一个哈希槽至节点的映射记录（map of hash slots to nodes）， 使得集群非常高效： 客户端可以直接向正确的节点发送命令请求， 无须转向、代理或者其他任何可能发生单点故障（single point failure）的实体（entiy）。</p>
<p>除了 <code>MOVED</code> 转向错误之外， 一个客户端还应该可以处理稍后介绍的 <code>ASK</code> 转向错误。</p>
<h2 id="集群在线重配置（live-reconfiguration）"><a href="#集群在线重配置（live-reconfiguration）" class="headerlink" title="集群在线重配置（live reconfiguration）"></a>集群在线重配置（live reconfiguration）</h2><p>Redis 集群支持在集群运行的过程中添加或者移除节点。</p>
<p>实际上， 节点的添加操作和节点的删除操作可以抽象成同一个操作， 那就是， 将哈希槽从一个节点移动到另一个节点：</p>
<ul>
<li>添加一个新节点到集群， 等于将其他已存在节点的槽移动到一个空白的新节点里面。</li>
<li>从集群中移除一个节点， 等于将被移除节点的所有槽移动到集群的其他节点上面去。</li>
</ul>
<p>因此， 实现 Redis 集群在线重配置的核心就是将槽从一个节点移动到另一个节点的能力。 因为一个哈希槽实际上就是一些键的集合， 所以 Redis 集群在重哈希（rehash）时真正要做的， 就是将一些键从一个节点移动到另一个节点。</p>
<p>要理解 Redis 集群如何将槽从一个节点移动到另一个节点， 我们需要对 <code>CLUSTER</code> 命令的各个子命令进行介绍， 这些命理负责管理集群节点的槽转换表（slots translation table）。</p>
<p>以下是 <code>CLUSTER</code> 命令可用的子命令：</p>
<ul>
<li><code>CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]</code></li>
<li><code>CLUSTER DELSLOTS slot1 [slot2] ... [slotN]</code></li>
<li><code>CLUSTER SETSLOT slot NODE node</code></li>
<li><code>CLUSTER SETSLOT slot MIGRATING node</code></li>
<li><code>CLUSTER SETSLOT slot IMPORTING node</code></li>
</ul>
<p>最开头的两条命令 <code>ADDSLOTS</code> 和 <code>DELSLOTS</code> 分别用于向节点指派（assign）或者移除节点， 当槽被指派或者移除之后， 节点会将这一信息通过 Gossip 协议传播到整个集群。 <code>ADDSLOTS</code> 命令通常在新创建集群时， 作为一种快速地将各个槽指派给各个节点的手段来使用。</p>
<p><code>CLUSTER SETSLOT slot NODE node</code> 子命令可以将指定的槽 <code>slot</code> 指派给节点 <code>node</code> 。</p>
<p>至于 <code>CLUSTER SETSLOT slot MIGRATING node</code> 命令和 <code>CLUSTER SETSLOT slot IMPORTING node</code> 命令， 前者用于将给定节点 <code>node</code> 中的槽 <code>slot</code> 迁移出节点， 而后者用于将给定槽 <code>slot</code> 导入到节点 <code>node</code> ：</p>
<ul>
<li><p>当一个槽被设置为 <code>MIGRATING</code> 状态时， 原来持有这个槽的节点仍然会继续接受关于这个槽的命令请求， 但只有命令所处理的键仍然存在于节点时， 节点才会处理这个命令请求。</p>
<p>如果命令所使用的键不存在与该节点， 那么节点将向客户端返回一个 <code>-ASK</code> 转向（redirection）错误， 告知客户端， 要将命令请求发送到槽的迁移目标节点。</p>
</li>
<li><p>当一个槽被设置为 <code>IMPORTING</code> 状态时， 节点仅在接收到 <code>ASKING</code> 命令之后， 才会接受关于这个槽的命令请求。</p>
<p>如果客户端没有向节点发送 <code>ASKING</code> 命令， 那么节点会使用 <code>-MOVED</code> 转向错误将命令请求转向至真正负责处理这个槽的节点。</p>
</li>
</ul>
<p>上面关于 <code>MIGRATING</code> 和 <code>IMPORTING</code> 的说明有些难懂， 让我们用一个实际的实例来说明一下。</p>
<p>假设现在， 我们有 A 和 B 两个节点， 并且我们想将槽 <code>8</code> 从节点 A 移动到节点 B ， 于是我们：</p>
<ul>
<li>向节点 B 发送命令 <code>CLUSTER SETSLOT 8 IMPORTING A</code></li>
<li>向节点 A 发送命令 <code>CLUSTER SETSLOT 8 MIGRATING B</code></li>
</ul>
<p>每当客户端向其他节点发送关于哈希槽 <code>8</code> 的命令请求时， 这些节点都会向客户端返回指向节点 A 的转向信息：</p>
<ul>
<li>如果命令要处理的键已经存在于槽 <code>8</code> 里面， 那么这个命令将由节点 A 处理。</li>
<li>如果命令要处理的键未存在于槽 <code>8</code> 里面（比如说，要向槽添加一个新的键）， 那么这个命令由节点 B 处理。</li>
</ul>
<p>这种机制将使得节点 A 不再创建关于槽 <code>8</code> 的任何新键。</p>
<p>与此同时， 一个特殊的客户端 <code>redis-trib</code> 以及 Redis 集群配置程序（configuration utility）会将节点 A 中槽 <code>8</code> 里面的键移动到节点 B 。</p>
<p>键的移动操作由以下两个命令执行：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CLUSTER GETKEYSINSLOT slot count</span><br></pre></td></tr></table></figure>

<p>上面的命令会让节点返回 <code>count</code> 个 <code>slot</code> 槽中的键， 对于命令所返回的每个键， <code>redis-trib</code> 都会向节点 A 发送一条 <em>MIGRATE</em> 命令， 该命令会将所指定的键原子地（atomic）从节点 A 移动到节点 B （在移动键期间，两个节点都会处于阻塞状态，以免出现竞争条件）。</p>
<p>以下为 <em>MIGRATE</em> 命令的运作原理：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MIGRATE target_host target_port key target_database id timeout</span><br></pre></td></tr></table></figure>

<p>执行 <em>MIGRATE</em> 命令的节点会连接到 <code>target</code> 节点， 并将序列化后的 <code>key</code> 数据发送给 <code>target</code> ， 一旦 <code>target</code> 返回 <code>OK</code> ， 节点就将自己的 <code>key</code> 从数据库中删除。</p>
<p>从一个外部客户端的视角来看， 在某个时间点上， 键 <code>key</code> 要么存在于节点 A ， 要么存在于节点 B ， 但不会同时存在于节点 A 和节点 B 。</p>
<p>因为 Redis 集群只使用 <code>0</code> 号数据库， 所以当 [<em>MIGRATE</em> 命令被用于执行集群操作时， <code>target_database</code> 的值总是 <code>0</code> 。</p>
<p><code>target_database</code> 参数的存在是为了让 [<em>MIGRATE</em> 命令成为一个通用命令， 从而可以作用于集群以外的其他功能。</p>
<p>我们对 <em>MIGRATE</em> 命令做了优化， 使得它即使在传输包含多个元素的列表键这样的复杂数据时， 也可以保持高效。</p>
<p>不过， 尽管 <em>MIGRATE</em> 非常高效， 对一个键非常多、并且键的数据量非常大的集群来说， 集群重配置还是会占用大量的时间， 可能会导致集群没办法适应那些对于响应时间有严格要求的应用程序。</p>
<h2 id="ASK-转向"><a href="#ASK-转向" class="headerlink" title="ASK 转向"></a>ASK 转向</h2><p>在之前介绍 <code>MOVED</code> 转向的时候， 我们说除了 <code>MOVED</code> 转向之外， 还有另一种 <code>ASK</code> 转向。</p>
<p>当节点需要让一个客户端长期地（permanently）将针对某个槽的命令请求发送至另一个节点时， 节点向客户端返回 <code>MOVED</code> 转向。</p>
<p>另一方面， 当节点需要让客户端仅仅在下一个命令请求中转向至另一个节点时， 节点向客户端返回 <code>ASK</code> 转向。</p>
<p>比如说， 在我们上一节列举的槽 <code>8</code> 的例子中， 因为槽 <code>8</code> 所包含的各个键分散在节点 A 和节点 B 中， 所以当客户端在节点 A 中没找到某个键时， 它应该转向到节点 B 中去寻找， 但是这种转向应该仅仅影响一次命令查询， 而不是让客户端每次都直接去查找节点 B ： 在节点 A 所持有的属于槽 <code>8</code> 的键没有全部被迁移到节点 B 之前， 客户端应该先访问节点 A ， 然后再访问节点 B 。</p>
<p>因为这种转向只针对 <code>16384</code> 个槽中的其中一个槽， 所以转向对集群造成的性能损耗属于可接受的范围。</p>
<p>因为上述原因， 如果我们要在查找节点 A 之后， 继续查找节点 B ， 那么客户端在向节点 B 发送命令请求之前， 应该先发送一个 <code>ASKING</code> 命令， 否则这个针对带有 <code>IMPORTING</code> 状态的槽的命令请求将被节点 B 拒绝执行。</p>
<p>接收到客户端 <code>ASKING</code> 命令的节点将为客户端设置一个一次性的标志（flag）， 使得客户端可以执行一次针对 <code>IMPORTING</code> 状态的槽的命令请求。</p>
<p>从客户端的角度来看， <code>ASK</code> 转向的完整语义（semantics）如下：</p>
<ul>
<li>如果客户端接收到 <code>ASK</code> 转向， 那么将命令请求的发送对象调整为转向所指定的节点。</li>
<li>先发送一个 <code>ASKING</code> 命令，然后再发送真正的命令请求。</li>
<li>不必更新客户端所记录的槽 <code>8</code> 至节点的映射： 槽 <code>8</code> 应该仍然映射到节点 A ， 而不是节点 B 。</li>
</ul>
<p>一旦节点 A 针对槽 <code>8</code> 的迁移工作完成， 节点 A 在再次收到针对槽 <code>8</code> 的命令请求时， 就会向客户端返回 <code>MOVED</code> 转向， 将关于槽 <code>8</code> 的命令请求长期地转向到节点 B 。</p>
<p>注意， 即使客户端出现 Bug ， 过早地将槽 <code>8</code> 映射到了节点 B 上面， 但只要这个客户端不发送 <code>ASKING</code> 命令， 客户端发送命令请求的时候就会遇上 <code>MOVED</code> 错误， 并将它转向回节点 A 。</p>
<h2 id="容错"><a href="#容错" class="headerlink" title="容错"></a>容错</h2><h3 id="节点失效检测"><a href="#节点失效检测" class="headerlink" title="节点失效检测"></a>节点失效检测</h3><p>以下是节点失效检查的实现方法：</p>
<ul>
<li><p>当一个节点向另一个节点发送 <em>PING</em> 命令， 但是目标节点未能在给定的时限内返回 [<em>PING</em> 命令的回复时， 那么发送命令的节点会将目标节点标记为 <code>PFAIL</code> （possible failure，可能已失效）。</p>
<p>等待 <em>PING</em> 命令回复的时限称为“节点超时时限（node timeout）”， 是一个节点选项（node-wise setting）。</p>
</li>
<li><p>每次当节点对其他节点发送 <em>PING</em> 命令的时候， 它都会随机地广播三个它所知道的节点的信息， 这些信息里面的其中一项就是说明节点是否已经被标记为 <code>PFAIL</code> 或者 <code>FAIL</code> 。</p>
</li>
<li><p>当节点接收到其他节点发来的信息时， 它会记下那些被其他节点标记为失效的节点。 这称为失效报告（failure report）。</p>
</li>
<li><p>如果节点已经将某个节点标记为 <code>PFAIL</code> ， 并且根据节点所收到的失效报告显式， 集群中的大部分其他主节点也认为那个节点进入了失效状态， 那么节点会将那个失效节点的状态标记为 <code>FAIL</code> 。</p>
</li>
<li><p>一旦某个节点被标记为 <code>FAIL</code> ， 关于这个节点已失效的信息就会被广播到整个集群， 所有接收到这条信息的节点都会将失效节点标记为 <code>FAIL</code> 。</p>
</li>
</ul>
<p>简单来说， 一个节点要将另一个节点标记为失效， 必须先询问其他节点的意见， 并且得到大部分主节点的同意才行。</p>
<p>因为过期的失效报告会被移除， 所以主节点要将某个节点标记为 <code>FAIL</code> 的话， 必须以最近接收到的失效报告作为根据。</p>
<p>在以下两种情况中， 节点的 <code>FAIL</code> 状态会被移除：</p>
<ul>
<li><p>如果被标记为 <code>FAIL</code> 的是从节点， 那么当这个节点重新上线时， <code>FAIL</code> 标记就会被移除。</p>
<p>保持（retaning）从节点的 <code>FAIL</code> 状态是没有意义的， 因为它不处理任何槽， 一个从节点是否处于 <code>FAIL</code> 状态， 决定了这个从节点在有需要时能否被提升为主节点。</p>
</li>
<li><p>如果一个主节点被打上 <code>FAIL</code> 标记之后， 经过了节点超时时限的四倍时间， 再加上十秒钟之后， 针对这个主节点的槽的故障转移操作仍未完成， 并且这个主节点已经重新上线的话， 那么移除对这个节点的 <code>FAIL</code> 标记。</p>
</li>
</ul>
<p>在第二种情况中， 如果故障转移未能顺利完成， 并且主节点重新上线， 那么集群就继续使用原来的主节点， 从而免去管理员介入的必要。</p>
<h3 id="集群状态检测（已部分实现）"><a href="#集群状态检测（已部分实现）" class="headerlink" title="集群状态检测（已部分实现）"></a>集群状态检测（已部分实现）</h3><p>每当集群发生配置变化时（可能是哈希槽更新，也可能是某个节点进入失效状态）， 集群中的每个节点都会对它所知道的节点进行扫描（scan）。</p>
<p>一旦配置处理完毕， 集群会进入以下两种状态的其中一种：</p>
<ul>
<li><code>FAIL</code> ： 集群不能正常工作。 当集群中有某个节点进入失效状态时， 集群不能处理任何命令请求， 对于每个命令请求， 集群节点都返回错误回复。</li>
<li><code>OK</code> ： 集群可以正常工作， 负责处理全部 <code>16384</code> 个槽的节点中， 没有一个节点被标记为 <code>FAIL</code> 状态。</li>
</ul>
<p>这说明即使集群中只有一部分哈希槽不能正常使用， 整个集群也会停止处理任何命令。</p>
<p>不过节点从出现问题到被标记为 <code>FAIL</code> 状态的这段时间里， 集群仍然会正常运作， 所以集群在某些时候， 仍然有可能只能处理针对 <code>16384</code> 个槽的其中一个子集的命令请求。</p>
<p>以下是集群进入 <code>FAIL</code> 状态的两种情况：</p>
<ol>
<li>至少有一个哈希槽不可用，因为负责处理这个槽的节点进入了 <code>FAIL</code> 状态。</li>
<li>集群中的大部分主节点都进入下线状态。当大部分主节点都进入 <code>PFAIL</code> 状态时，集群也会进入 <code>FAIL</code> 状态。</li>
</ol>
<p>第二个检查是必须的， 因为要将一个节点从 <code>PFAIL</code> 状态改变为 <code>FAIL</code> 状态， 必须要有大部分主节点进行投票表决， 但是， 当集群中的大部分主节点都进入失效状态时， 单凭一个两个节点是没有办法将一个节点标记为 <code>FAIL</code> 状态的。</p>
<p>因此， 有了第二个检查条件， 只要集群中的大部分主节点进入了下线状态， 那么集群就可以在不请求这些主节点的意见下， 将某个节点判断为 <code>FAIL</code> 状态， 从而让整个集群停止处理命令请求。</p>
<h3 id="从节点选举"><a href="#从节点选举" class="headerlink" title="从节点选举"></a>从节点选举</h3><p>一旦某个主节点进入 <code>FAIL</code> 状态， 如果这个主节点有一个或多个从节点存在， 那么其中一个从节点会被升级为新的主节点， 而其他从节点则会开始对这个新的主节点进行复制。</p>
<p>新的主节点由已下线主节点属下的所有从节点中自行选举产生， 以下是选举的条件：</p>
<ul>
<li>这个节点是已下线主节点的从节点。</li>
<li>已下线主节点负责处理的槽数量非空。</li>
<li>从节点的数据被认为是可靠的， 也即是， 主从节点之间的复制连接（replication link）的断线时长不能超过节点超时时限（node timeout）乘以 <code>REDIS_CLUSTER_SLAVE_VALIDITY_MULT</code> 常量得出的积。</li>
</ul>
<p>如果一个从节点满足了以上的所有条件， 那么这个从节点将向集群中的其他主节点发送授权请求， 询问它们， 是否允许自己（从节点）升级为新的主节点。</p>
<p>如果发送授权请求的从节点满足以下属性， 那么主节点将向从节点返回 <code>FAILOVER_AUTH_GRANTED</code> 授权， 同意从节点的升级要求：</p>
<ul>
<li>发送授权请求的是一个从节点， 并且它所属的主节点处于 <code>FAIL</code> 状态。</li>
<li>在已下线主节点的所有从节点中， 这个从节点的节点 ID 在排序中是最小的。</li>
<li>这个从节点处于正常的运行状态： 它没有被标记为 <code>FAIL</code> 状态， 也没有被标记为 <code>PFAIL</code> 状态。</li>
</ul>
<p>一旦某个从节点在给定的时限内得到大部分主节点的授权， 它就会开始执行以下故障转移操作：</p>
<ul>
<li>通过 <code>PONG</code> 数据包（packet）告知其他节点， 这个节点现在是主节点了。</li>
<li>通过 <code>PONG</code> 数据包告知其他节点， 这个节点是一个已升级的从节点（promoted slave）。</li>
<li>接管（claiming）所有由已下线主节点负责处理的哈希槽。</li>
<li>显式地向所有节点广播一个 <code>PONG</code> 数据包， 加速其他节点识别这个节点的进度， 而不是等待定时的 <code>PING</code> / <code>PONG</code> 数据包。</li>
</ul>
<p>所有其他节点都会根据新的主节点对配置进行相应的更新，特别地：</p>
<ul>
<li>所有被新的主节点接管的槽会被更新。</li>
<li>已下线主节点的所有从节点会察觉到 <code>PROMOTED</code> 标志， 并开始对新的主节点进行复制。</li>
<li>如果已下线的主节点重新回到上线状态， 那么它会察觉到 <code>PROMOTED</code> 标志， 并将自身调整为现任主节点的从节点。</li>
</ul>
<p>在集群的生命周期中， 如果一个带有 <code>PROMOTED</code> 标识的主节点因为某些原因转变成了从节点， 那么该节点将丢失它所带有的 <code>PROMOTED</code> 标识。</p>
<h2 id="发布-订阅（已实现，但仍然需要改善）"><a href="#发布-订阅（已实现，但仍然需要改善）" class="headerlink" title="发布/订阅（已实现，但仍然需要改善）"></a>发布/订阅（已实现，但仍然需要改善）</h2><p>在一个 Redis 集群中， 客户端可以订阅任意一个节点， 也可以向任意一个节点发送信息， 节点会对客户端所发送的信息进行转发。</p>
<p>在目前的实现中， 节点会将接收到的信息广播至集群中的其他所有节点， 在将来的实现中， 可能会使用 bloom filter 或者其他算法来优化这一操作。</p>
<h2 id="附录-A：-CRC16-算法的-ANSI-实现参考"><a href="#附录-A：-CRC16-算法的-ANSI-实现参考" class="headerlink" title="附录 A： CRC16 算法的 ANSI 实现参考"></a>附录 A： CRC16 算法的 ANSI 实现参考</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * Copyright 2001-2010 Georges Menie (www.menie.org)</span><br><span class="line"> * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style)</span><br><span class="line"> * All rights reserved.</span><br><span class="line"> * Redistribution and use <span class="keyword">in</span> <span class="built_in">source</span> and binary forms, with or without</span><br><span class="line"> * modification, are permitted provided that the following conditions are met:</span><br><span class="line"> *</span><br><span class="line"> *     * Redistributions of <span class="built_in">source</span> code must retain the above copyright</span><br><span class="line"> *       notice, this list of conditions and the following disclaimer.</span><br><span class="line"> *     * Redistributions <span class="keyword">in</span> binary form must reproduce the above copyright</span><br><span class="line"> *       notice, this list of conditions and the following disclaimer <span class="keyword">in</span> the</span><br><span class="line"> *       documentation and/or other materials provided with the distribution.</span><br><span class="line"> *     * Neither the name of the University of California, Berkeley nor the</span><br><span class="line"> *       names of its contributors may be used to endorse or promote products</span><br><span class="line"> *       derived from this software without specific prior written permission.</span><br><span class="line"> *</span><br><span class="line"> * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS<span class="string">''</span> AND ANY</span><br><span class="line"> * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span><br><span class="line"> * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</span><br><span class="line"> * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY</span><br><span class="line"> * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES</span><br><span class="line"> * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;</span><br><span class="line"> * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND</span><br><span class="line"> * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT</span><br><span class="line"> * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS</span><br><span class="line"> * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">/* CRC16 implementation acording to CCITT standards.</span><br><span class="line"> *</span><br><span class="line"> * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the</span><br><span class="line"> * following parameters:</span><br><span class="line"> *</span><br><span class="line"> * Name                       : <span class="string">"XMODEM"</span>, also known as <span class="string">"ZMODEM"</span>, <span class="string">"CRC-16/ACORN"</span></span><br><span class="line"> * Width                      : 16 bit</span><br><span class="line"> * Poly                       : 1021 (That is actually x^16 + x^12 + x^5 + 1)</span><br><span class="line"> * Initialization             : 0000</span><br><span class="line"> * Reflect Input byte         : False</span><br><span class="line"> * Reflect Output CRC         : False</span><br><span class="line"> * Xor constant to output CRC : 0000</span><br><span class="line"> * Output <span class="keyword">for</span> <span class="string">"123456789"</span>     : 31C3</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">static const uint16_t crc16tab[256]= &#123;</span><br><span class="line">    0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,</span><br><span class="line">    0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,</span><br><span class="line">    0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,</span><br><span class="line">    0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,</span><br><span class="line">    0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,</span><br><span class="line">    0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,</span><br><span class="line">    0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,</span><br><span class="line">    0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,</span><br><span class="line">    0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,</span><br><span class="line">    0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,</span><br><span class="line">    0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,</span><br><span class="line">    0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,</span><br><span class="line">    0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,</span><br><span class="line">    0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,</span><br><span class="line">    0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,</span><br><span class="line">    0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,</span><br><span class="line">    0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,</span><br><span class="line">    0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,</span><br><span class="line">    0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,</span><br><span class="line">    0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,</span><br><span class="line">    0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,</span><br><span class="line">    0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,</span><br><span class="line">    0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,</span><br><span class="line">    0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,</span><br><span class="line">    0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,</span><br><span class="line">    0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,</span><br><span class="line">    0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,</span><br><span class="line">    0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,</span><br><span class="line">    0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,</span><br><span class="line">    0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,</span><br><span class="line">    0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,</span><br><span class="line">    0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">uint16_t crc16(const char *buf, int len) &#123;</span><br><span class="line">    int counter;</span><br><span class="line">    uint16_t crc = 0;</span><br><span class="line">    <span class="keyword">for</span> (counter = 0; counter &lt; len; counter++)</span><br><span class="line">            crc = (crc&lt;&lt;8) ^ crc16tab[((crc&gt;&gt;8) ^ *buf++)&amp;0x00FF];</span><br><span class="line">    <span class="built_in">return</span> crc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


    </div>

    
    
    

    
    <div>    
      
        <ul class="post-copyright">
          <li class="post-copyright-link">
          <strong>本文作者：</strong>
          <a href="/" title="欢迎访问 Chenyumeng 的个人博客">Chenyumeng</a>
          </li>

          <li class="post-copyright-link">
            <strong>本文标题：</strong>
            <a href="https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/" title="Redis所有命令+集群（翻译转载）">Redis所有命令+集群（翻译转载）</a>
          </li>

          <li class="post-copyright-link">
            <strong>本文链接：</strong>
            <a href="https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/" title="Redis所有命令+集群（翻译转载）">https://ipvb.gitee.io/blog/2020/03/15/Redis_translate/</a>
          </li>

          <li class="post-copyright-date">
            <strong>发布时间： </strong>2020年3月15日 - 16时03分
          </li>  

          <li class="post-copyright-license">
            <strong>版权声明： </strong>
            本文由 Chenyumeng 原创。
          </li>
        </ul>
      
    </div>
      
  <div class="popular-posts-header">相关文章推荐</div>
  <ul class="popular-posts">
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="\blog\2020\03\15\centOs7_Redis\" rel="bookmark">centOs7下安装Redis记录</a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="\blog\2020\03\16\linux MySQL安装\" rel="bookmark">linux MySQL安装</a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="\blog\2020\03\16\redis集群搭建\" rel="bookmark">redis集群搭建</a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="\blog\2020\03\15\ZooKeeper\" rel="bookmark">ZooKeeper单机与集群安装笔记</a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="\blog\2020\03\16\linux JDK安装\" rel="bookmark">linux JDK安装</a></div>
    </li>
  </ul>

        <div class="reward-container">
  <div></div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/blog/images/wechatpay.png" alt="Chenyumeng 微信支付">
        <p>微信支付</p>
      </div>
      
      <div style="display: inline-block;">
        <img src="/blog/images/alipay.png" alt="Chenyumeng 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>


      <div>
        
          <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------　　　　本文结束　<i class="fa fa-heart"></i>　感谢您的阅读　　　　-------------</div>
    
</div>
        
      </div>

      <footer class="post-footer">
          
          <div class="post-tags">
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
              <a href="/blog/" rel="tag"><i class="fa fa-tag"></i> </a>
            
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/blog/2020/03/15/nginx/" rel="prev" title="nginx安装配置笔记">
      <i class="fa fa-chevron-left"></i> nginx安装配置笔记
    </a></div>
      <div class="post-nav-item">
    <a href="/blog/2020/03/15/centOs7_Redis/" rel="next" title="centOs7下安装Redis记录">
      centOs7下安装Redis记录 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  

    <!-- 引入share.js -->
    
      <div data-weibo-title="分享到微博" data-qq-title="分享到QQ" data-douban-title="分享到豆瓣" class="social-share" class="share-component" data-disabled="twitter,facebook" data-description="Share.js - 一键分享到微博，QQ空间，腾讯微博，人人，豆瓣">分享到：</div>
    
  </div>


          </div>
          
    
  <div class="comments">
    <div id="lv-container" data-id="city" data-uid="MTAyMC80OTExNy8yNTYxMg=="></div>
  </div>
  

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#一、Key（键）"><span class="nav-number">1.</span> <span class="nav-text">一、Key（键）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-DEL"><span class="nav-number">1.1.</span> <span class="nav-text">1.DEL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-DUMP"><span class="nav-number">1.2.</span> <span class="nav-text">2.DUMP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-EXISTS"><span class="nav-number">1.3.</span> <span class="nav-text">3.EXISTS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-EXPIRE"><span class="nav-number">1.4.</span> <span class="nav-text">4.EXPIRE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-EXPIREAT"><span class="nav-number">1.5.</span> <span class="nav-text">5.EXPIREAT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-KEYS"><span class="nav-number">1.6.</span> <span class="nav-text">6.KEYS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-MIGRATE"><span class="nav-number">1.7.</span> <span class="nav-text">7.MIGRATE</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#示例"><span class="nav-number">1.7.1.</span> <span class="nav-text">示例</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-MIGRATE"><span class="nav-number">1.8.</span> <span class="nav-text">8.MIGRATE</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#示例-1"><span class="nav-number">1.8.1.</span> <span class="nav-text">示例</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-MOVE"><span class="nav-number">1.9.</span> <span class="nav-text">9.MOVE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-OBJECT"><span class="nav-number">1.10.</span> <span class="nav-text">10.OBJECT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-PERSIST"><span class="nav-number">1.11.</span> <span class="nav-text">11.PERSIST</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-PEXPIRE"><span class="nav-number">1.12.</span> <span class="nav-text">12.PEXPIRE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-PEXPIREAT"><span class="nav-number">1.13.</span> <span class="nav-text">13.PEXPIREAT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-PTTL"><span class="nav-number">1.14.</span> <span class="nav-text">14.PTTL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-RANDOMKEY"><span class="nav-number">1.15.</span> <span class="nav-text">15.RANDOMKEY</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16-RENAME"><span class="nav-number">1.16.</span> <span class="nav-text">16.RENAME</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17-RENAMENX"><span class="nav-number">1.17.</span> <span class="nav-text">17.RENAMENX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#18-RESTORE"><span class="nav-number">1.18.</span> <span class="nav-text">18.RESTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#19-SORT"><span class="nav-number">1.19.</span> <span class="nav-text">19.SORT</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#一般-SORT-用法"><span class="nav-number">1.19.1.</span> <span class="nav-text">一般 SORT 用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用-ALPHA-修饰符对字符串进行排序"><span class="nav-number">1.19.2.</span> <span class="nav-text">使用 ALPHA 修饰符对字符串进行排序</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用-LIMIT-修饰符限制返回结果"><span class="nav-number">1.19.3.</span> <span class="nav-text">使用 LIMIT 修饰符限制返回结果</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用外部-key-进行排序"><span class="nav-number">1.19.4.</span> <span class="nav-text">使用外部 key 进行排序</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#BY-选项"><span class="nav-number">1.19.4.1.</span> <span class="nav-text">BY 选项</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#GET-选项"><span class="nav-number">1.19.4.2.</span> <span class="nav-text">GET 选项</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#组合使用-BY-和-GET"><span class="nav-number">1.19.4.3.</span> <span class="nav-text">组合使用 BY 和 GET</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#获取多个外部键"><span class="nav-number">1.19.4.4.</span> <span class="nav-text">获取多个外部键</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#获取外部键，但不进行排序"><span class="nav-number">1.19.4.5.</span> <span class="nav-text">获取外部键，但不进行排序</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#将哈希表作为-GET-或-BY-的参数"><span class="nav-number">1.19.4.6.</span> <span class="nav-text">将哈希表作为 GET 或 BY 的参数</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#保存排序结果"><span class="nav-number">1.19.5.</span> <span class="nav-text">保存排序结果</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#20-TTL"><span class="nav-number">1.20.</span> <span class="nav-text">20.TTL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-TYPE"><span class="nav-number">1.21.</span> <span class="nav-text">21.TYPE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#22-SCAN"><span class="nav-number">1.22.</span> <span class="nav-text">22.SCAN</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#SCAN-命令的基本用法"><span class="nav-number">1.22.1.</span> <span class="nav-text">SCAN 命令的基本用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#SCAN-命令的保证（guarantees）"><span class="nav-number">1.22.2.</span> <span class="nav-text">SCAN 命令的保证（guarantees）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#SCAN-命令每次执行返回的元素数量"><span class="nav-number">1.22.3.</span> <span class="nav-text">SCAN 命令每次执行返回的元素数量</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#COUNT-选项"><span class="nav-number">1.22.4.</span> <span class="nav-text">COUNT 选项</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#MATCH-选项"><span class="nav-number">1.22.5.</span> <span class="nav-text">MATCH 选项</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#并发执行多个迭代"><span class="nav-number">1.22.6.</span> <span class="nav-text">并发执行多个迭代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#中途停止迭代"><span class="nav-number">1.22.7.</span> <span class="nav-text">中途停止迭代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用错误的游标进行增量式迭代"><span class="nav-number">1.22.8.</span> <span class="nav-text">使用错误的游标进行增量式迭代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#迭代终结的保证"><span class="nav-number">1.22.9.</span> <span class="nav-text">迭代终结的保证</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#二、String（字符串）"><span class="nav-number">2.</span> <span class="nav-text">二、String（字符串）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-APPEND"><span class="nav-number">2.1.</span> <span class="nav-text">1.APPEND</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：时间序列-Time-series"><span class="nav-number">2.1.1.</span> <span class="nav-text">模式：时间序列(Time series)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-BITCOUNT"><span class="nav-number">2.2.</span> <span class="nav-text">2.BITCOUNT</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：使用-bitmap-实现用户上线次数统计"><span class="nav-number">2.2.1.</span> <span class="nav-text">模式：使用 bitmap 实现用户上线次数统计</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#性能"><span class="nav-number">2.2.2.</span> <span class="nav-text">性能</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-BITOP"><span class="nav-number">2.3.</span> <span class="nav-text">3.BITOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-DECR"><span class="nav-number">2.4.</span> <span class="nav-text">4.DECR</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-DECRBY"><span class="nav-number">2.5.</span> <span class="nav-text">5.DECRBY</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-GET"><span class="nav-number">2.6.</span> <span class="nav-text">6.GET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-GETBIT"><span class="nav-number">2.7.</span> <span class="nav-text">7.GETBIT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-GETRANGE"><span class="nav-number">2.8.</span> <span class="nav-text">8.GETRANGE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-GETSET"><span class="nav-number">2.9.</span> <span class="nav-text">9.GETSET</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式"><span class="nav-number">2.9.1.</span> <span class="nav-text">模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-INCR"><span class="nav-number">2.10.</span> <span class="nav-text">10.INCR</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：计数器"><span class="nav-number">2.10.1.</span> <span class="nav-text">模式：计数器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：限速器"><span class="nav-number">2.10.2.</span> <span class="nav-text">模式：限速器</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-INCRBY"><span class="nav-number">2.11.</span> <span class="nav-text">11.INCRBY</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-INCRBYFLOAT"><span class="nav-number">2.12.</span> <span class="nav-text">12.INCRBYFLOAT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-MGET"><span class="nav-number">2.13.</span> <span class="nav-text">13.MGET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-MSET"><span class="nav-number">2.14.</span> <span class="nav-text">14.MSET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-MSETNX"><span class="nav-number">2.15.</span> <span class="nav-text">15.MSETNX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16-PSETEX"><span class="nav-number">2.16.</span> <span class="nav-text">16.PSETEX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17-SET"><span class="nav-number">2.17.</span> <span class="nav-text">17.SET</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#使用模式"><span class="nav-number">2.17.1.</span> <span class="nav-text">使用模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#18-SETBIT"><span class="nav-number">2.18.</span> <span class="nav-text">18.SETBIT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#19-SETEX"><span class="nav-number">2.19.</span> <span class="nav-text">19.SETEX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#20-SETNX"><span class="nav-number">2.20.</span> <span class="nav-text">20.SETNX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-SETRANGE"><span class="nav-number">2.21.</span> <span class="nav-text">21.SETRANGE</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式-1"><span class="nav-number">2.21.1.</span> <span class="nav-text">模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#22-STRLEN"><span class="nav-number">2.22.</span> <span class="nav-text">22.STRLEN</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#三、Hash（哈希表）"><span class="nav-number">3.</span> <span class="nav-text">三、Hash（哈希表）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-HDEL"><span class="nav-number">3.1.</span> <span class="nav-text">1.HDEL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-HEXISTS"><span class="nav-number">3.2.</span> <span class="nav-text">2.HEXISTS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-HGET"><span class="nav-number">3.3.</span> <span class="nav-text">3.HGET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-HGETALL"><span class="nav-number">3.4.</span> <span class="nav-text">4.HGETALL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-HINCRBY"><span class="nav-number">3.5.</span> <span class="nav-text">5.HINCRBY</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-HINCRBYFLOAT"><span class="nav-number">3.6.</span> <span class="nav-text">6.HINCRBYFLOAT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-HKEYS"><span class="nav-number">3.7.</span> <span class="nav-text">7.HKEYS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-HLEN"><span class="nav-number">3.8.</span> <span class="nav-text">8.HLEN</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-HMGET"><span class="nav-number">3.9.</span> <span class="nav-text">9.HMGET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-HMSET"><span class="nav-number">3.10.</span> <span class="nav-text">10.HMSET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-HSET"><span class="nav-number">3.11.</span> <span class="nav-text">11.HSET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-HSETNX"><span class="nav-number">3.12.</span> <span class="nav-text">12.HSETNX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-HVALS"><span class="nav-number">3.13.</span> <span class="nav-text">13.HVALS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-HSCAN"><span class="nav-number">3.14.</span> <span class="nav-text">14.HSCAN</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#四、List（列表）"><span class="nav-number">4.</span> <span class="nav-text">四、List（列表）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-BLPOP"><span class="nav-number">4.1.</span> <span class="nav-text">1.BLPOP</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：事件提醒"><span class="nav-number">4.1.1.</span> <span class="nav-text">模式：事件提醒</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-BRPOP"><span class="nav-number">4.2.</span> <span class="nav-text">2.BRPOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-BRPOPLPUSH"><span class="nav-number">4.3.</span> <span class="nav-text">3.BRPOPLPUSH</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：安全队列"><span class="nav-number">4.3.1.</span> <span class="nav-text">模式：安全队列</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：循环列表"><span class="nav-number">4.3.2.</span> <span class="nav-text">模式：循环列表</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-LINDEX"><span class="nav-number">4.4.</span> <span class="nav-text">4.LINDEX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-LINSERT"><span class="nav-number">4.5.</span> <span class="nav-text">5.LINSERT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-LLEN"><span class="nav-number">4.6.</span> <span class="nav-text">6.LLEN</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-LPOP"><span class="nav-number">4.7.</span> <span class="nav-text">7.LPOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-LPUSH"><span class="nav-number">4.8.</span> <span class="nav-text">8.LPUSH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-LPUSHX"><span class="nav-number">4.9.</span> <span class="nav-text">9.LPUSHX</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-LRANGE"><span class="nav-number">4.10.</span> <span class="nav-text">10.LRANGE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-LREM"><span class="nav-number">4.11.</span> <span class="nav-text">11.LREM</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-LSET"><span class="nav-number">4.12.</span> <span class="nav-text">12.LSET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-LTRIM"><span class="nav-number">4.13.</span> <span class="nav-text">13.LTRIM</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-RPOP"><span class="nav-number">4.14.</span> <span class="nav-text">14.RPOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-RPOPLPUSH"><span class="nav-number">4.15.</span> <span class="nav-text">15.RPOPLPUSH</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：-安全的队列"><span class="nav-number">4.15.1.</span> <span class="nav-text">模式： 安全的队列</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#模式：循环列表-1"><span class="nav-number">4.15.2.</span> <span class="nav-text">模式：循环列表</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16-RPUSH"><span class="nav-number">4.16.</span> <span class="nav-text">16.RPUSH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17-RPUSHX"><span class="nav-number">4.17.</span> <span class="nav-text">17.RPUSHX</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#五、Set（集合）"><span class="nav-number">5.</span> <span class="nav-text">五、Set（集合）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-SADD"><span class="nav-number">5.1.</span> <span class="nav-text">1.SADD</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-SCARD"><span class="nav-number">5.2.</span> <span class="nav-text">2.SCARD</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-SDIFF"><span class="nav-number">5.3.</span> <span class="nav-text">3.SDIFF</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-SDIFFSTORE"><span class="nav-number">5.4.</span> <span class="nav-text">4.SDIFFSTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-SINTER"><span class="nav-number">5.5.</span> <span class="nav-text">5.SINTER</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-SINTERSTORE"><span class="nav-number">5.6.</span> <span class="nav-text">6.SINTERSTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-SISMEMBER"><span class="nav-number">5.7.</span> <span class="nav-text">7.SISMEMBER</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-SMEMBERS"><span class="nav-number">5.8.</span> <span class="nav-text">8.SMEMBERS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-SMOVE"><span class="nav-number">5.9.</span> <span class="nav-text">9.SMOVE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-SPOP"><span class="nav-number">5.10.</span> <span class="nav-text">10.SPOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-SRANDMEMBER"><span class="nav-number">5.11.</span> <span class="nav-text">11.SRANDMEMBER</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-SREM"><span class="nav-number">5.12.</span> <span class="nav-text">12.SREM</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-SUNION"><span class="nav-number">5.13.</span> <span class="nav-text">13.SUNION</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-SUNIONSTORE"><span class="nav-number">5.14.</span> <span class="nav-text">14.SUNIONSTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-SSCAN"><span class="nav-number">5.15.</span> <span class="nav-text">15.SSCAN</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#六、SortedSet（有序集合）"><span class="nav-number">6.</span> <span class="nav-text">六、SortedSet（有序集合）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-ZADD"><span class="nav-number">6.1.</span> <span class="nav-text">1.ZADD</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-ZCARD"><span class="nav-number">6.2.</span> <span class="nav-text">2.ZCARD</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-ZCOUNT"><span class="nav-number">6.3.</span> <span class="nav-text">3.ZCOUNT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-ZINCRBY"><span class="nav-number">6.4.</span> <span class="nav-text">4.ZINCRBY</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-ZRANGE"><span class="nav-number">6.5.</span> <span class="nav-text">5.ZRANGE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-ZRANGEBYSCORE"><span class="nav-number">6.6.</span> <span class="nav-text">6.ZRANGEBYSCORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-ZRANK"><span class="nav-number">6.7.</span> <span class="nav-text">7.ZRANK</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-ZREM"><span class="nav-number">6.8.</span> <span class="nav-text">8.ZREM</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-ZREMRANGEBYRANK"><span class="nav-number">6.9.</span> <span class="nav-text">9.ZREMRANGEBYRANK</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-ZREMRANGEBYSCORE"><span class="nav-number">6.10.</span> <span class="nav-text">10.ZREMRANGEBYSCORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-ZREVRANGE"><span class="nav-number">6.11.</span> <span class="nav-text">11.ZREVRANGE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-ZREVRANGEBYSCORE"><span class="nav-number">6.12.</span> <span class="nav-text">12.ZREVRANGEBYSCORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-ZREVRANK"><span class="nav-number">6.13.</span> <span class="nav-text">13.ZREVRANK</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-ZSCORE"><span class="nav-number">6.14.</span> <span class="nav-text">14.ZSCORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-ZUNIONSTORE"><span class="nav-number">6.15.</span> <span class="nav-text">15.ZUNIONSTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16-ZINTERSTORE"><span class="nav-number">6.16.</span> <span class="nav-text">16.ZINTERSTORE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17-ZSCAN"><span class="nav-number">6.17.</span> <span class="nav-text">17.ZSCAN</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#七、Pub-Sub（发布-订阅）"><span class="nav-number">7.</span> <span class="nav-text">七、Pub&#x2F;Sub（发布&#x2F;订阅）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-PSUBSCRIBE"><span class="nav-number">7.1.</span> <span class="nav-text">1.PSUBSCRIBE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-PUBLISH"><span class="nav-number">7.2.</span> <span class="nav-text">2.PUBLISH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-PUBSUB"><span class="nav-number">7.3.</span> <span class="nav-text">3.PUBSUB</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#PUBSUB-CHANNELS-pattern"><span class="nav-number">7.3.1.</span> <span class="nav-text">PUBSUB CHANNELS [pattern]</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#PUBSUB-NUMSUB-channel-1-…-channel-N"><span class="nav-number">7.3.2.</span> <span class="nav-text">PUBSUB NUMSUB [channel-1 … channel-N]</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#PUBSUB-NUMPAT"><span class="nav-number">7.3.3.</span> <span class="nav-text">PUBSUB NUMPAT</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-PUNSUBSCRIBE"><span class="nav-number">7.4.</span> <span class="nav-text">4.PUNSUBSCRIBE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-SUBSCRIBE"><span class="nav-number">7.5.</span> <span class="nav-text">5.SUBSCRIBE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-UNSUBSCRIBE"><span class="nav-number">7.6.</span> <span class="nav-text">6.UNSUBSCRIBE</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#八、Transaction（事务）"><span class="nav-number">8.</span> <span class="nav-text">八、Transaction（事务）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-DISCARD"><span class="nav-number">8.1.</span> <span class="nav-text">1.DISCARD</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-EXEC"><span class="nav-number">8.2.</span> <span class="nav-text">2.EXEC</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-MULTI"><span class="nav-number">8.3.</span> <span class="nav-text">3.MULTI</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-UNWATCH"><span class="nav-number">8.4.</span> <span class="nav-text">4.UNWATCH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-WATCH"><span class="nav-number">8.5.</span> <span class="nav-text">5.WATCH</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#九、Script（脚本）"><span class="nav-number">9.</span> <span class="nav-text">九、Script（脚本）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-EVAL"><span class="nav-number">9.1.</span> <span class="nav-text">1.EVAL</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#在-Lua-数据类型和-Redis-数据类型之间转换"><span class="nav-number">9.1.1.</span> <span class="nav-text">在 Lua 数据类型和 Redis 数据类型之间转换</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#脚本的原子性"><span class="nav-number">9.1.2.</span> <span class="nav-text">脚本的原子性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#错误处理"><span class="nav-number">9.1.3.</span> <span class="nav-text">错误处理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#带宽和-EVALSHA"><span class="nav-number">9.1.4.</span> <span class="nav-text">带宽和 EVALSHA</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#脚本缓存"><span class="nav-number">9.1.5.</span> <span class="nav-text">脚本缓存</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#SCRIPT-命令"><span class="nav-number">9.1.6.</span> <span class="nav-text">SCRIPT 命令</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#纯函数脚本"><span class="nav-number">9.1.7.</span> <span class="nav-text">纯函数脚本</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#全局变量保护"><span class="nav-number">9.1.8.</span> <span class="nav-text">全局变量保护</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#库"><span class="nav-number">9.1.9.</span> <span class="nav-text">库</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用脚本散发-Redis-日志"><span class="nav-number">9.1.10.</span> <span class="nav-text">使用脚本散发 Redis 日志</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#沙箱-sandbox-和最大执行时间"><span class="nav-number">9.2.</span> <span class="nav-text">沙箱(sandbox)和最大执行时间</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#流水线-pipeline-上下文-context-中的-EVALSHA"><span class="nav-number">9.2.1.</span> <span class="nav-text">流水线(pipeline)上下文(context)中的 EVALSHA</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-EVALSHA"><span class="nav-number">9.3.</span> <span class="nav-text">2.EVALSHA</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-SCRIPT-EXISTS"><span class="nav-number">9.4.</span> <span class="nav-text">3.SCRIPT EXISTS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-SCRIPT-FLUSH"><span class="nav-number">9.5.</span> <span class="nav-text">4.SCRIPT FLUSH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-SCRIPT-KILL"><span class="nav-number">9.6.</span> <span class="nav-text">5.SCRIPT KILL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-SCRIPT-LOAD"><span class="nav-number">9.7.</span> <span class="nav-text">6.SCRIPT LOAD</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十、Connection（连接）"><span class="nav-number">10.</span> <span class="nav-text">十、Connection（连接）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-AUTH"><span class="nav-number">10.1.</span> <span class="nav-text">1.AUTH</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-ECHO"><span class="nav-number">10.2.</span> <span class="nav-text">2.ECHO</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-PING"><span class="nav-number">10.3.</span> <span class="nav-text">3.PING</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-QUIT"><span class="nav-number">10.4.</span> <span class="nav-text">4.QUIT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-SELECT"><span class="nav-number">10.5.</span> <span class="nav-text">5.SELECT</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十一、Server（服务器）"><span class="nav-number">11.</span> <span class="nav-text">十一、Server（服务器）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-BGREWRITEAOF"><span class="nav-number">11.1.</span> <span class="nav-text">1.BGREWRITEAOF</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-BGSAVE"><span class="nav-number">11.2.</span> <span class="nav-text">2.BGSAVE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-CLIENT-GETNAME"><span class="nav-number">11.3.</span> <span class="nav-text">3.CLIENT GETNAME</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-CLIENT-KILL"><span class="nav-number">11.4.</span> <span class="nav-text">4.CLIENT KILL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-CLIENT-LIST"><span class="nav-number">11.5.</span> <span class="nav-text">5.CLIENT LIST</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-CLIENT-SETNAME"><span class="nav-number">11.6.</span> <span class="nav-text">6.CLIENT SETNAME</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-CONFIG-GET"><span class="nav-number">11.7.</span> <span class="nav-text">7.CONFIG GET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-CONFIG-RESETSTAT"><span class="nav-number">11.8.</span> <span class="nav-text">8.CONFIG RESETSTAT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-CONFIG-REWRITE"><span class="nav-number">11.9.</span> <span class="nav-text">9.CONFIG REWRITE</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#原子性重写"><span class="nav-number">11.9.1.</span> <span class="nav-text">原子性重写</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#可用版本"><span class="nav-number">11.9.2.</span> <span class="nav-text">可用版本</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#返回值"><span class="nav-number">11.9.3.</span> <span class="nav-text">返回值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#测试"><span class="nav-number">11.9.4.</span> <span class="nav-text">测试</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-CONFIG-SET"><span class="nav-number">11.10.</span> <span class="nav-text">10.CONFIG SET</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-DBSIZE"><span class="nav-number">11.11.</span> <span class="nav-text">11.DBSIZE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#12-DEBUG-OBJECT"><span class="nav-number">11.12.</span> <span class="nav-text">12.DEBUG OBJECT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#13-DEBUG-SEGFAULT"><span class="nav-number">11.13.</span> <span class="nav-text">13.DEBUG SEGFAULT</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#14-FLUSHALL"><span class="nav-number">11.14.</span> <span class="nav-text">14.FLUSHALL</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#15-FLUSHDB"><span class="nav-number">11.15.</span> <span class="nav-text">15.FLUSHDB</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#16-INFO"><span class="nav-number">11.16.</span> <span class="nav-text">16.INFO</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#17-LASTSAVE"><span class="nav-number">11.17.</span> <span class="nav-text">17.LASTSAVE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#18-MONITOR"><span class="nav-number">11.18.</span> <span class="nav-text">18.MONITOR</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#19-PSYNC"><span class="nav-number">11.19.</span> <span class="nav-text">19.PSYNC</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#20-SAVE"><span class="nav-number">11.20.</span> <span class="nav-text">20.SAVE</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#21-SHUTDOWN"><span class="nav-number">11.21.</span> <span class="nav-text">21.SHUTDOWN</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#22-SLAVEOF"><span class="nav-number">11.22.</span> <span class="nav-text">22.SLAVEOF</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#23-SLOWLOG"><span class="nav-number">11.23.</span> <span class="nav-text">23.SLOWLOG</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#24-SYNC"><span class="nav-number">11.24.</span> <span class="nav-text">24.SYNC</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#25-TIME"><span class="nav-number">11.25.</span> <span class="nav-text">25.TIME</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十二、集群教程"><span class="nav-number">12.</span> <span class="nav-text">十二、集群教程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#集群简介"><span class="nav-number">12.1.</span> <span class="nav-text">集群简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis-集群数据共享"><span class="nav-number">12.2.</span> <span class="nav-text">Redis 集群数据共享</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis-集群中的主从复制"><span class="nav-number">12.3.</span> <span class="nav-text">Redis 集群中的主从复制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis-集群的一致性保证（guarantee）"><span class="nav-number">12.4.</span> <span class="nav-text">Redis 集群的一致性保证（guarantee）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建并使用-Redis-集群"><span class="nav-number">12.5.</span> <span class="nav-text">创建并使用 Redis 集群</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建集群"><span class="nav-number">12.6.</span> <span class="nav-text">创建集群</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#集群的客户端"><span class="nav-number">12.7.</span> <span class="nav-text">集群的客户端</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用-redis-rb-cluster-编写一个示例应用"><span class="nav-number">12.8.</span> <span class="nav-text">使用 redis-rb-cluster 编写一个示例应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#对集群进行重新分片"><span class="nav-number">12.9.</span> <span class="nav-text">对集群进行重新分片</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#一个更有趣的示例应用"><span class="nav-number">12.10.</span> <span class="nav-text">一个更有趣的示例应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#故障转移测试"><span class="nav-number">12.11.</span> <span class="nav-text">故障转移测试</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#添加新节点到集群"><span class="nav-number">12.12.</span> <span class="nav-text">添加新节点到集群</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#移除一个节点"><span class="nav-number">12.13.</span> <span class="nav-text">移除一个节点</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#十二、Redis-集群规范"><span class="nav-number">13.</span> <span class="nav-text">十二、Redis 集群规范</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#引言"><span class="nav-number">13.1.</span> <span class="nav-text">引言</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#什么是-Redis-集群？"><span class="nav-number">13.2.</span> <span class="nav-text">什么是 Redis 集群？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis-集群实现的功能子集"><span class="nav-number">13.3.</span> <span class="nav-text">Redis 集群实现的功能子集</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis-集群协议中的客户端和服务器"><span class="nav-number">13.4.</span> <span class="nav-text">Redis 集群协议中的客户端和服务器</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#键分布模型"><span class="nav-number">13.5.</span> <span class="nav-text">键分布模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#集群节点属性"><span class="nav-number">13.6.</span> <span class="nav-text">集群节点属性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#节点握手（已实现）"><span class="nav-number">13.7.</span> <span class="nav-text">节点握手（已实现）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MOVED-转向"><span class="nav-number">13.8.</span> <span class="nav-text">MOVED 转向</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#集群在线重配置（live-reconfiguration）"><span class="nav-number">13.9.</span> <span class="nav-text">集群在线重配置（live reconfiguration）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ASK-转向"><span class="nav-number">13.10.</span> <span class="nav-text">ASK 转向</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#容错"><span class="nav-number">13.11.</span> <span class="nav-text">容错</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#节点失效检测"><span class="nav-number">13.11.1.</span> <span class="nav-text">节点失效检测</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#集群状态检测（已部分实现）"><span class="nav-number">13.11.2.</span> <span class="nav-text">集群状态检测（已部分实现）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#从节点选举"><span class="nav-number">13.11.3.</span> <span class="nav-text">从节点选举</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#发布-订阅（已实现，但仍然需要改善）"><span class="nav-number">13.12.</span> <span class="nav-text">发布&#x2F;订阅（已实现，但仍然需要改善）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#附录-A：-CRC16-算法的-ANSI-实现参考"><span class="nav-number">13.13.</span> <span class="nav-text">附录 A： CRC16 算法的 ANSI 实现参考</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Chenyumeng"
      src="/blog/images/touxiang.JPG">
  <p class="site-author-name" itemprop="name">Chenyumeng</p>
  <div class="site-description" itemprop="description">用来记录自己学习中所遇到的问题以及如何解决和自己所学知识的理解</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blog/archives/">
        
          <span class="site-state-item-count">32</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/blog/categories/">
          
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/blog/tags/">
          
        <span class="site-state-item-count">29</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/chen-yumeng" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;chen-yumeng" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/u/3123221441" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;u&#x2F;3123221441" rel="noopener" target="_blank"><i class="fa fa-fw fa-weibo"></i>Weibo</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title">
      <i class="fa fa-fw fa-link"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://github.com/" title="https:&#x2F;&#x2F;github.com&#x2F;" rel="noopener" target="_blank">Github</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Chenyumeng</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    <span title="站点总字数">390k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">5:55</span>
</div>

        






  <script>
  function leancloudSelector(url) {
    url = encodeURI(url);
    return document.getElementById(url).querySelector('.leancloud-visitors-count');
  }
  if (CONFIG.page.isPost) {
    function addCount(Counter) {
      var visitors = document.querySelector('.leancloud_visitors');
      var url = decodeURI(visitors.id);
      var title = visitors.dataset.flagTitle;

      Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({ url })))
        .then(response => response.json())
        .then(({ results }) => {
          if (results.length > 0) {
            var counter = results[0];
              leancloudSelector(url).innerText = counter.time + 1;
            Counter('put', '/classes/Counter/' + counter.objectId, { time: { '__op': 'Increment', 'amount': 1 } })
              .then(response => response.json())
              .catch(error => {
                console.error('Failed to save visitor count', error);
              })
          } else {
              Counter('post', '/classes/Counter', { title, url, time: 1 })
                .then(response => response.json())
                .then(() => {
                  leancloudSelector(url).innerText = 1;
                })
                .catch(error => {
                  console.error('Failed to create', error);
                });
          }
        })
        .catch(error => {
          console.error('LeanCloud Counter Error', error);
        });
    }
  } else {
    function showTime(Counter) {
      var visitors = document.querySelectorAll('.leancloud_visitors');
      var entries = [...visitors].map(element => {
        return decodeURI(element.id);
      });

      Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({ url: { '$in': entries } })))
        .then(response => response.json())
        .then(({ results }) => {
          for (let url of entries) {
            let target = results.find(item => item.url === url);
            leancloudSelector(url).innerText = target ? target.time : 0;
          }
        })
        .catch(error => {
          console.error('LeanCloud Counter Error', error);
        });
    }
  }

  fetch('https://app-router.leancloud.cn/2/route?appId=')
    .then(response => response.json())
    .then(({ api_server }) => {
      var Counter = (method, url, data) => {
        return fetch(`https://${api_server}/1.1${url}`, {
          method,
          headers: {
            'X-LC-Id'     : '',
            'X-LC-Key'    : '',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };
      if (CONFIG.page.isPost) {
        if (CONFIG.hostname !== location.hostname) return;
        addCount(Counter);
      } else if (document.querySelectorAll('.post-title-link').length >= 1) {
        showTime(Counter);
      }
    });
  </script>


      </div>
    </footer>
  </div>

  
  <script src="/blog/lib/anime.min.js"></script>
  <script src="/blog/lib/velocity/velocity.min.js"></script>
  <script src="/blog/lib/velocity/velocity.ui.min.js"></script>

<script src="/blog/js/utils.js"></script>

<script src="/blog/js/motion.js"></script>


<script src="/blog/js/schemes/pisces.js"></script>


<script src="/blog/js/next-boot.js"></script>




  




  
<script src="/blog/js/local-search.js"></script>













  

  

<script>
NexT.utils.loadComments(document.querySelector('#lv-container'), () => {
  window.livereOptions = {
    refer: location.pathname.replace(CONFIG.root, '').replace('index.html', '')
  };
  (function(d, s) {
    var j, e = d.getElementsByTagName(s)[0];
    if (typeof LivereTower === 'function') { return; }
    j = d.createElement(s);
    j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
    j.async = true;
    e.parentNode.insertBefore(j, e);
  })(document, 'script');
});
</script>

  <script src="/dist/js/social-share.min.js"></script>
</body>
</html>
